From edf94ae5c16618031389de05b93e4cf48f04464f Mon Sep 17 00:00:00 2001 From: Dodo Date: Thu, 11 Jun 2026 18:41:42 +0700 Subject: [PATCH] feat: initial commit with seeded database and premium ui changes --- .gitignore | 53 + backend/.env.example | 4 + backend/Dockerfile | 12 + backend/bun.lock | 218 + backend/package-lock.json | 940 ++ backend/package.json | 18 + backend/src/app.js | 27 + backend/src/db-init.js | 253 + backend/src/db.js | 7 + backend/src/routes/auth.routes.js | 71 + backend/src/routes/gas-station.routes.js | 339 + backend/src/routes/households.routes.js | 311 + backend/src/routes/land.routes.js | 233 + backend/src/routes/pertamina-fallback.json | 1 + backend/src/routes/poi.routes.js | 112 + backend/src/seed-data.js | 358 + backend/src/server.js | 16 + backend/src/users.js | 11 + docker-compose.coolify.yml | 69 + docker-compose.yml | 53 + frontend/.gitignore | 41 + frontend/AGENTS.md | 5 + frontend/CLAUDE.md | 1 + frontend/Dockerfile.frontend | 26 + frontend/README.md | 36 + frontend/app/auth/login/page.tsx | 5 + frontend/app/dashboard/gas-station/page.tsx | 322 + frontend/app/dashboard/land/page.tsx | 400 + frontend/app/dashboard/page.tsx | 27 + frontend/app/dashboard/poverty/page.tsx | 389 + frontend/app/dashboard/settings/page.tsx | 229 + frontend/app/favicon.ico | Bin 0 -> 15406 bytes frontend/app/globals.css | 181 + frontend/app/layout.tsx | 41 + frontend/app/page.tsx | 19 + frontend/bun.lock | 1560 +++ frontend/components.json | 27 + frontend/components/app-breadcrumbs.tsx | 32 + frontend/components/app-header.tsx | 47 + frontend/components/app-shared.tsx | 121 + frontend/components/app-shell.tsx | 39 + frontend/components/app-sidebar.tsx | 88 + frontend/components/auth-divider.tsx | 16 + frontend/components/auth-page.tsx | 251 + .../components/custom-sidebar-trigger.tsx | 22 + frontend/components/dashboard-activity.tsx | 80 + frontend/components/dashboard-card.tsx | 15 + frontend/components/dashboard-skeleton.tsx | 20 + frontend/components/dashboard.tsx | 109 + frontend/components/decor-icon.tsx | 45 + frontend/components/delta.tsx | 179 + frontend/components/floating-paths.tsx | 53 + frontend/components/formater.ts | 95 + .../gas-station/gas-station-directory.tsx | 151 + .../gas-station/gas-station-hud.tsx | 52 + .../gas-station/gas-station-inspector.tsx | 166 + .../gas-station/gas-station-stats-cards.tsx | 117 + .../gas-station/pertamina-price-table.tsx | 156 + .../gas-station/station-edit-dialog.tsx | 571 + .../gas-station/station-placement-dialog.tsx | 449 + frontend/components/header.tsx | 65 + .../components/households-trend-chart.tsx | 128 + frontend/components/icons/apple-icon.tsx | 18 + frontend/components/icons/github-icon.tsx | 10 + frontend/components/icons/google-icon.tsx | 14 + .../components/land/land-allocation-chart.tsx | 79 + frontend/components/land/land-directory.tsx | 96 + frontend/components/land/land-inspector.tsx | 168 + frontend/components/land/land-stats-cards.tsx | 55 + .../components/land/shape-edit-dialog.tsx | 338 + .../land/shape-placement-dialog.tsx | 306 + frontend/components/landing/landing-about.tsx | 111 + frontend/components/landing/landing-cta.tsx | 96 + .../components/landing/landing-footer.tsx | 96 + .../components/landing/landing-header.tsx | 128 + frontend/components/landing/landing-hero.tsx | 80 + .../components/landing/landing-projects.tsx | 97 + frontend/components/landing/link-icon.tsx | 19 + frontend/components/latest-change.tsx | 48 + frontend/components/leaflet-map.tsx | 810 ++ frontend/components/logo.tsx | 26 + frontend/components/maps/map-page-header.tsx | 35 + frontend/components/maps/maps-breadcrumb.tsx | 82 + frontend/components/mobile-nav.tsx | 54 + frontend/components/nav-group.tsx | 48 + frontend/components/nav-user.tsx | 66 + frontend/components/portal.tsx | 52 + .../poverty/fuzzy-priority-stats.tsx | 120 + .../components/poverty/fuzzy-result-step.tsx | 138 + .../components/poverty/marker-edit-dialog.tsx | 473 + .../poverty/marker-placement-dialog.tsx | 393 + .../components/poverty/poverty-directory.tsx | 82 + .../poverty/poverty-households-table.tsx | 190 + frontend/components/poverty/poverty-hud.tsx | 26 + .../components/poverty/poverty-inspector.tsx | 90 + .../poverty/poverty-stats-cards.tsx | 71 + .../poverty/poverty-trend-chart.tsx | 86 + frontend/components/poverty/radius-slider.tsx | 54 + .../components/poverty/religion-dialog.tsx | 224 + frontend/components/priority-breakdown.tsx | 101 + frontend/components/priority-score-chart.tsx | 137 + frontend/components/recent-households.tsx | 87 + .../breadcrumb/breadcrumb-03.tsx | 44 + frontend/components/stats.tsx | 89 + frontend/components/time-undo-action.tsx | 197 + frontend/components/ui/adaptive-slider.tsx | 192 + frontend/components/ui/avatar.tsx | 109 + frontend/components/ui/badge.tsx | 52 + frontend/components/ui/breadcrumb.tsx | 125 + frontend/components/ui/button.tsx | 58 + frontend/components/ui/card.tsx | 103 + frontend/components/ui/chart.tsx | 383 + frontend/components/ui/collapsible.tsx | 21 + .../ui/collection-grid-disclosure.tsx | 540 + frontend/components/ui/continuous-tabs.tsx | 119 + frontend/components/ui/cosmic-button.tsx | 113 + frontend/components/ui/dialog.tsx | 160 + frontend/components/ui/dropdown-menu.tsx | 268 + frontend/components/ui/dropdown.tsx | 145 + frontend/components/ui/empty.tsx | 104 + frontend/components/ui/input-group.tsx | 158 + frontend/components/ui/input.tsx | 20 + frontend/components/ui/kbd.tsx | 26 + frontend/components/ui/phosphor-icons.tsx | 709 ++ frontend/components/ui/separator.tsx | 25 + frontend/components/ui/sheet.tsx | 138 + frontend/components/ui/sidebar.tsx | 725 ++ frontend/components/ui/skeleton.tsx | 13 + frontend/components/ui/sonner.tsx | 49 + frontend/components/ui/table.tsx | 116 + frontend/components/ui/textarea.tsx | 18 + frontend/components/ui/tooltip.tsx | 66 + frontend/design/design.md | 400 + frontend/design/tailwind.css | 64 + frontend/design/token.json | 374 + frontend/design/variables.css | 89 + .../2026-06-10-religion-dialog-radius-fix.md | 772 ++ ...06-10-religion-dialog-radius-fix-design.md | 83 + frontend/eslint.config.mjs | 18 + frontend/hooks/use-mobile.ts | 19 + frontend/hooks/use-scroll.ts | 28 + frontend/lib/auth-context.tsx | 73 + frontend/lib/auth-types.ts | 18 + frontend/lib/dashboard-types.ts | 16 + frontend/lib/map-data.ts | 264 + frontend/lib/poverty-types.ts | 27 + frontend/lib/utils.ts | 6 + frontend/next.config.ts | 43 + frontend/package-lock.json | 9204 +++++++++++++++++ frontend/package.json | 49 + frontend/postcss.config.mjs | 7 + frontend/proxy.ts | 65 + frontend/public/file.svg | 1 + frontend/public/globe.svg | 1 + frontend/public/light/acorn-light.svg | 1 + frontend/public/light/address-book-light.svg | 1 + .../public/light/address-book-tabs-light.svg | 1 + .../light/air-traffic-control-light.svg | 1 + .../public/light/airplane-in-flight-light.svg | 1 + .../public/light/airplane-landing-light.svg | 1 + frontend/public/light/airplane-light.svg | 1 + .../public/light/airplane-takeoff-light.svg | 1 + .../public/light/airplane-taxiing-light.svg | 1 + frontend/public/light/airplane-tilt-light.svg | 1 + frontend/public/light/airplay-light.svg | 1 + frontend/public/light/alarm-light.svg | 1 + frontend/public/light/alien-light.svg | 1 + frontend/public/light/align-bottom-light.svg | 1 + .../light/align-bottom-simple-light.svg | 1 + .../light/align-center-horizontal-light.svg | 1 + .../align-center-horizontal-simple-light.svg | 1 + .../light/align-center-vertical-light.svg | 1 + .../align-center-vertical-simple-light.svg | 1 + frontend/public/light/align-left-light.svg | 1 + .../public/light/align-left-simple-light.svg | 1 + frontend/public/light/align-right-light.svg | 1 + .../public/light/align-right-simple-light.svg | 1 + frontend/public/light/align-top-light.svg | 1 + .../public/light/align-top-simple-light.svg | 1 + frontend/public/light/amazon-logo-light.svg | 1 + frontend/public/light/ambulance-light.svg | 1 + frontend/public/light/anchor-light.svg | 1 + frontend/public/light/anchor-simple-light.svg | 1 + frontend/public/light/android-logo-light.svg | 1 + frontend/public/light/angle-light.svg | 1 + frontend/public/light/angular-logo-light.svg | 1 + frontend/public/light/aperture-light.svg | 1 + .../public/light/app-store-logo-light.svg | 1 + frontend/public/light/app-window-light.svg | 1 + frontend/public/light/apple-logo-light.svg | 1 + .../light/apple-podcasts-logo-light.svg | 1 + .../public/light/approximate-equals-light.svg | 1 + frontend/public/light/archive-light.svg | 1 + frontend/public/light/armchair-light.svg | 1 + .../public/light/arrow-arc-left-light.svg | 1 + .../public/light/arrow-arc-right-light.svg | 1 + .../light/arrow-bend-double-up-left-light.svg | 1 + .../arrow-bend-double-up-right-light.svg | 1 + .../light/arrow-bend-down-left-light.svg | 1 + .../light/arrow-bend-down-right-light.svg | 1 + .../light/arrow-bend-left-down-light.svg | 1 + .../public/light/arrow-bend-left-up-light.svg | 1 + .../light/arrow-bend-right-down-light.svg | 1 + .../light/arrow-bend-right-up-light.svg | 1 + .../public/light/arrow-bend-up-left-light.svg | 1 + .../light/arrow-bend-up-right-light.svg | 1 + .../light/arrow-circle-down-left-light.svg | 1 + .../public/light/arrow-circle-down-light.svg | 1 + .../light/arrow-circle-down-right-light.svg | 1 + .../public/light/arrow-circle-left-light.svg | 1 + .../public/light/arrow-circle-right-light.svg | 1 + .../light/arrow-circle-up-left-light.svg | 1 + .../public/light/arrow-circle-up-light.svg | 1 + .../light/arrow-circle-up-right-light.svg | 1 + .../public/light/arrow-clockwise-light.svg | 1 + .../light/arrow-counter-clockwise-light.svg | 1 + .../public/light/arrow-down-left-light.svg | 1 + frontend/public/light/arrow-down-light.svg | 1 + .../public/light/arrow-down-right-light.svg | 1 + .../light/arrow-elbow-down-left-light.svg | 1 + .../light/arrow-elbow-down-right-light.svg | 1 + .../light/arrow-elbow-left-down-light.svg | 1 + .../public/light/arrow-elbow-left-light.svg | 1 + .../light/arrow-elbow-left-up-light.svg | 1 + .../light/arrow-elbow-right-down-light.svg | 1 + .../public/light/arrow-elbow-right-light.svg | 1 + .../light/arrow-elbow-right-up-light.svg | 1 + .../light/arrow-elbow-up-left-light.svg | 1 + .../light/arrow-elbow-up-right-light.svg | 1 + .../public/light/arrow-fat-down-light.svg | 1 + .../public/light/arrow-fat-left-light.svg | 1 + .../light/arrow-fat-line-down-light.svg | 1 + .../light/arrow-fat-line-left-light.svg | 1 + .../light/arrow-fat-line-right-light.svg | 1 + .../public/light/arrow-fat-line-up-light.svg | 1 + .../light/arrow-fat-lines-down-light.svg | 1 + .../light/arrow-fat-lines-left-light.svg | 1 + .../light/arrow-fat-lines-right-light.svg | 1 + .../public/light/arrow-fat-lines-up-light.svg | 1 + .../public/light/arrow-fat-right-light.svg | 1 + frontend/public/light/arrow-fat-up-light.svg | 1 + frontend/public/light/arrow-left-light.svg | 1 + .../light/arrow-line-down-left-light.svg | 1 + .../public/light/arrow-line-down-light.svg | 1 + .../light/arrow-line-down-right-light.svg | 1 + .../public/light/arrow-line-left-light.svg | 1 + .../public/light/arrow-line-right-light.svg | 1 + .../public/light/arrow-line-up-left-light.svg | 1 + frontend/public/light/arrow-line-up-light.svg | 1 + .../light/arrow-line-up-right-light.svg | 1 + frontend/public/light/arrow-right-light.svg | 1 + .../light/arrow-square-down-left-light.svg | 1 + .../public/light/arrow-square-down-light.svg | 1 + .../light/arrow-square-down-right-light.svg | 1 + .../public/light/arrow-square-in-light.svg | 1 + .../public/light/arrow-square-left-light.svg | 1 + .../public/light/arrow-square-out-light.svg | 1 + .../public/light/arrow-square-right-light.svg | 1 + .../light/arrow-square-up-left-light.svg | 1 + .../public/light/arrow-square-up-light.svg | 1 + .../light/arrow-square-up-right-light.svg | 1 + .../public/light/arrow-u-down-left-light.svg | 1 + .../public/light/arrow-u-down-right-light.svg | 1 + .../public/light/arrow-u-left-down-light.svg | 1 + .../public/light/arrow-u-left-up-light.svg | 1 + .../public/light/arrow-u-right-down-light.svg | 1 + .../public/light/arrow-u-right-up-light.svg | 1 + .../public/light/arrow-u-up-left-light.svg | 1 + .../public/light/arrow-u-up-right-light.svg | 1 + frontend/public/light/arrow-up-left-light.svg | 1 + frontend/public/light/arrow-up-light.svg | 1 + .../public/light/arrow-up-right-light.svg | 1 + .../public/light/arrows-clockwise-light.svg | 1 + .../light/arrows-counter-clockwise-light.svg | 1 + .../public/light/arrows-down-up-light.svg | 1 + .../public/light/arrows-horizontal-light.svg | 1 + .../public/light/arrows-in-cardinal-light.svg | 1 + frontend/public/light/arrows-in-light.svg | 1 + .../light/arrows-in-line-horizontal-light.svg | 1 + .../light/arrows-in-line-vertical-light.svg | 1 + .../public/light/arrows-in-simple-light.svg | 1 + .../public/light/arrows-left-right-light.svg | 1 + frontend/public/light/arrows-merge-light.svg | 1 + .../light/arrows-out-cardinal-light.svg | 1 + frontend/public/light/arrows-out-light.svg | 1 + .../arrows-out-line-horizontal-light.svg | 1 + .../light/arrows-out-line-vertical-light.svg | 1 + .../public/light/arrows-out-simple-light.svg | 1 + frontend/public/light/arrows-split-light.svg | 1 + .../public/light/arrows-vertical-light.svg | 1 + frontend/public/light/article-light.svg | 1 + .../public/light/article-medium-light.svg | 1 + .../public/light/article-ny-times-light.svg | 1 + frontend/public/light/asclepius-light.svg | 1 + frontend/public/light/asterisk-light.svg | 1 + .../public/light/asterisk-simple-light.svg | 1 + frontend/public/light/at-light.svg | 1 + frontend/public/light/atom-light.svg | 1 + frontend/public/light/avocado-light.svg | 1 + frontend/public/light/axe-light.svg | 1 + frontend/public/light/baby-carriage-light.svg | 1 + frontend/public/light/baby-light.svg | 1 + frontend/public/light/backpack-light.svg | 1 + frontend/public/light/backspace-light.svg | 1 + frontend/public/light/bag-light.svg | 1 + frontend/public/light/bag-simple-light.svg | 1 + frontend/public/light/balloon-light.svg | 1 + frontend/public/light/bandaids-light.svg | 1 + frontend/public/light/bank-light.svg | 1 + frontend/public/light/barbell-light.svg | 1 + frontend/public/light/barcode-light.svg | 1 + frontend/public/light/barn-light.svg | 1 + frontend/public/light/barricade-light.svg | 1 + frontend/public/light/baseball-cap-light.svg | 1 + .../public/light/baseball-helmet-light.svg | 1 + frontend/public/light/baseball-light.svg | 1 + frontend/public/light/basket-light.svg | 1 + frontend/public/light/basketball-light.svg | 1 + frontend/public/light/bathtub-light.svg | 1 + .../public/light/battery-charging-light.svg | 1 + .../light/battery-charging-vertical-light.svg | 1 + frontend/public/light/battery-empty-light.svg | 1 + frontend/public/light/battery-full-light.svg | 1 + frontend/public/light/battery-high-light.svg | 1 + frontend/public/light/battery-low-light.svg | 1 + .../public/light/battery-medium-light.svg | 1 + frontend/public/light/battery-plus-light.svg | 1 + .../light/battery-plus-vertical-light.svg | 1 + .../light/battery-vertical-empty-light.svg | 1 + .../light/battery-vertical-full-light.svg | 1 + .../light/battery-vertical-high-light.svg | 1 + .../light/battery-vertical-low-light.svg | 1 + .../light/battery-vertical-medium-light.svg | 1 + .../public/light/battery-warning-light.svg | 1 + .../light/battery-warning-vertical-light.svg | 1 + frontend/public/light/beach-ball-light.svg | 1 + frontend/public/light/beanie-light.svg | 1 + frontend/public/light/bed-light.svg | 1 + frontend/public/light/beer-bottle-light.svg | 1 + frontend/public/light/beer-stein-light.svg | 1 + frontend/public/light/behance-logo-light.svg | 1 + frontend/public/light/bell-light.svg | 1 + frontend/public/light/bell-ringing-light.svg | 1 + frontend/public/light/bell-simple-light.svg | 1 + .../light/bell-simple-ringing-light.svg | 1 + .../public/light/bell-simple-slash-light.svg | 1 + frontend/public/light/bell-simple-z-light.svg | 1 + frontend/public/light/bell-slash-light.svg | 1 + frontend/public/light/bell-z-light.svg | 1 + frontend/public/light/belt-light.svg | 1 + frontend/public/light/bezier-curve-light.svg | 1 + frontend/public/light/bicycle-light.svg | 1 + frontend/public/light/binary-light.svg | 1 + frontend/public/light/binoculars-light.svg | 1 + frontend/public/light/biohazard-light.svg | 1 + frontend/public/light/bird-light.svg | 1 + frontend/public/light/blueprint-light.svg | 1 + .../light/bluetooth-connected-light.svg | 1 + frontend/public/light/bluetooth-light.svg | 1 + .../public/light/bluetooth-slash-light.svg | 1 + frontend/public/light/bluetooth-x-light.svg | 1 + frontend/public/light/boat-light.svg | 1 + frontend/public/light/bomb-light.svg | 1 + frontend/public/light/bone-light.svg | 1 + frontend/public/light/book-bookmark-light.svg | 1 + frontend/public/light/book-light.svg | 1 + frontend/public/light/book-open-light.svg | 1 + .../public/light/book-open-text-light.svg | 1 + .../public/light/book-open-user-light.svg | 1 + frontend/public/light/bookmark-light.svg | 1 + .../public/light/bookmark-simple-light.svg | 1 + frontend/public/light/bookmarks-light.svg | 1 + .../public/light/bookmarks-simple-light.svg | 1 + frontend/public/light/books-light.svg | 1 + frontend/public/light/boot-light.svg | 1 + frontend/public/light/boules-light.svg | 1 + frontend/public/light/bounding-box-light.svg | 1 + frontend/public/light/bowl-food-light.svg | 1 + frontend/public/light/bowl-steam-light.svg | 1 + frontend/public/light/bowling-ball-light.svg | 1 + .../public/light/box-arrow-down-light.svg | 1 + frontend/public/light/box-arrow-up-light.svg | 1 + frontend/public/light/boxing-glove-light.svg | 1 + .../public/light/brackets-angle-light.svg | 1 + .../public/light/brackets-curly-light.svg | 1 + .../public/light/brackets-round-light.svg | 1 + .../public/light/brackets-square-light.svg | 1 + frontend/public/light/brain-light.svg | 1 + frontend/public/light/brandy-light.svg | 1 + frontend/public/light/bread-light.svg | 1 + frontend/public/light/bridge-light.svg | 1 + frontend/public/light/briefcase-light.svg | 1 + .../public/light/briefcase-metal-light.svg | 1 + frontend/public/light/broadcast-light.svg | 1 + frontend/public/light/broom-light.svg | 1 + frontend/public/light/browser-light.svg | 1 + frontend/public/light/browsers-light.svg | 1 + frontend/public/light/bug-beetle-light.svg | 1 + frontend/public/light/bug-droid-light.svg | 1 + frontend/public/light/bug-light.svg | 1 + .../public/light/building-apartment-light.svg | 1 + frontend/public/light/building-light.svg | 1 + .../public/light/building-office-light.svg | 1 + frontend/public/light/buildings-light.svg | 1 + frontend/public/light/bulldozer-light.svg | 1 + frontend/public/light/bus-light.svg | 1 + frontend/public/light/butterfly-light.svg | 1 + frontend/public/light/cable-car-light.svg | 1 + frontend/public/light/cactus-light.svg | 1 + frontend/public/light/cake-light.svg | 1 + frontend/public/light/calculator-light.svg | 1 + .../public/light/calendar-blank-light.svg | 1 + .../public/light/calendar-check-light.svg | 1 + frontend/public/light/calendar-dot-light.svg | 1 + frontend/public/light/calendar-dots-light.svg | 1 + .../public/light/calendar-heart-light.svg | 1 + frontend/public/light/calendar-light.svg | 1 + .../public/light/calendar-minus-light.svg | 1 + frontend/public/light/calendar-plus-light.svg | 1 + .../public/light/calendar-slash-light.svg | 1 + frontend/public/light/calendar-star-light.svg | 1 + frontend/public/light/calendar-x-light.svg | 1 + frontend/public/light/call-bell-light.svg | 1 + frontend/public/light/camera-light.svg | 1 + frontend/public/light/camera-plus-light.svg | 1 + frontend/public/light/camera-rotate-light.svg | 1 + frontend/public/light/camera-slash-light.svg | 1 + frontend/public/light/campfire-light.svg | 1 + frontend/public/light/car-battery-light.svg | 1 + frontend/public/light/car-light.svg | 1 + frontend/public/light/car-profile-light.svg | 1 + frontend/public/light/car-simple-light.svg | 1 + frontend/public/light/cardholder-light.svg | 1 + frontend/public/light/cards-light.svg | 1 + frontend/public/light/cards-three-light.svg | 1 + .../light/caret-circle-double-down-light.svg | 1 + .../light/caret-circle-double-left-light.svg | 1 + .../light/caret-circle-double-right-light.svg | 1 + .../light/caret-circle-double-up-light.svg | 1 + .../public/light/caret-circle-down-light.svg | 1 + .../public/light/caret-circle-left-light.svg | 1 + .../public/light/caret-circle-right-light.svg | 1 + .../light/caret-circle-up-down-light.svg | 1 + .../public/light/caret-circle-up-light.svg | 1 + .../public/light/caret-double-down-light.svg | 1 + .../public/light/caret-double-left-light.svg | 1 + .../public/light/caret-double-right-light.svg | 1 + .../public/light/caret-double-up-light.svg | 1 + frontend/public/light/caret-down-light.svg | 1 + frontend/public/light/caret-left-light.svg | 1 + .../public/light/caret-line-down-light.svg | 1 + .../public/light/caret-line-left-light.svg | 1 + .../public/light/caret-line-right-light.svg | 1 + frontend/public/light/caret-line-up-light.svg | 1 + frontend/public/light/caret-right-light.svg | 1 + frontend/public/light/caret-up-down-light.svg | 1 + frontend/public/light/caret-up-light.svg | 1 + frontend/public/light/carrot-light.svg | 1 + frontend/public/light/cash-register-light.svg | 1 + frontend/public/light/cassette-tape-light.svg | 1 + frontend/public/light/castle-turret-light.svg | 1 + frontend/public/light/cat-light.svg | 1 + .../public/light/cell-signal-full-light.svg | 1 + .../public/light/cell-signal-high-light.svg | 1 + .../public/light/cell-signal-low-light.svg | 1 + .../public/light/cell-signal-medium-light.svg | 1 + .../public/light/cell-signal-none-light.svg | 1 + .../public/light/cell-signal-slash-light.svg | 1 + frontend/public/light/cell-signal-x-light.svg | 1 + frontend/public/light/cell-tower-light.svg | 1 + frontend/public/light/certificate-light.svg | 1 + frontend/public/light/chair-light.svg | 1 + frontend/public/light/chalkboard-light.svg | 1 + .../public/light/chalkboard-simple-light.svg | 1 + .../public/light/chalkboard-teacher-light.svg | 1 + frontend/public/light/champagne-light.svg | 1 + .../public/light/charging-station-light.svg | 1 + .../light/chart-bar-horizontal-light.svg | 1 + frontend/public/light/chart-bar-light.svg | 1 + frontend/public/light/chart-donut-light.svg | 1 + .../public/light/chart-line-down-light.svg | 1 + frontend/public/light/chart-line-light.svg | 1 + frontend/public/light/chart-line-up-light.svg | 1 + frontend/public/light/chart-pie-light.svg | 1 + .../public/light/chart-pie-slice-light.svg | 1 + frontend/public/light/chart-polar-light.svg | 1 + frontend/public/light/chart-scatter-light.svg | 1 + .../public/light/chat-centered-dots-light.svg | 1 + frontend/public/light/chat-centered-light.svg | 1 + .../light/chat-centered-slash-light.svg | 1 + .../public/light/chat-centered-text-light.svg | 1 + .../public/light/chat-circle-dots-light.svg | 1 + frontend/public/light/chat-circle-light.svg | 1 + .../public/light/chat-circle-slash-light.svg | 1 + .../public/light/chat-circle-text-light.svg | 1 + frontend/public/light/chat-dots-light.svg | 1 + frontend/public/light/chat-light.svg | 1 + frontend/public/light/chat-slash-light.svg | 1 + .../public/light/chat-teardrop-dots-light.svg | 1 + frontend/public/light/chat-teardrop-light.svg | 1 + .../light/chat-teardrop-slash-light.svg | 1 + .../public/light/chat-teardrop-text-light.svg | 1 + frontend/public/light/chat-text-light.svg | 1 + frontend/public/light/chats-circle-light.svg | 1 + frontend/public/light/chats-light.svg | 1 + .../public/light/chats-teardrop-light.svg | 1 + frontend/public/light/check-circle-light.svg | 1 + frontend/public/light/check-fat-light.svg | 1 + frontend/public/light/check-light.svg | 1 + frontend/public/light/check-square-light.svg | 1 + .../light/check-square-offset-light.svg | 1 + frontend/public/light/checkerboard-light.svg | 1 + frontend/public/light/checks-light.svg | 1 + frontend/public/light/cheers-light.svg | 1 + frontend/public/light/cheese-light.svg | 1 + frontend/public/light/chef-hat-light.svg | 1 + frontend/public/light/cherries-light.svg | 1 + frontend/public/light/church-light.svg | 1 + frontend/public/light/cigarette-light.svg | 1 + .../public/light/cigarette-slash-light.svg | 1 + frontend/public/light/circle-dashed-light.svg | 1 + frontend/public/light/circle-half-light.svg | 1 + .../public/light/circle-half-tilt-light.svg | 1 + frontend/public/light/circle-light.svg | 1 + frontend/public/light/circle-notch-light.svg | 1 + frontend/public/light/circles-four-light.svg | 1 + frontend/public/light/circles-three-light.svg | 1 + .../public/light/circles-three-plus-light.svg | 1 + frontend/public/light/circuitry-light.svg | 1 + frontend/public/light/city-light.svg | 1 + frontend/public/light/clipboard-light.svg | 1 + .../public/light/clipboard-text-light.svg | 1 + .../public/light/clock-afternoon-light.svg | 1 + .../public/light/clock-clockwise-light.svg | 1 + .../public/light/clock-countdown-light.svg | 1 + .../light/clock-counter-clockwise-light.svg | 1 + frontend/public/light/clock-light.svg | 1 + frontend/public/light/clock-user-light.svg | 1 + .../public/light/closed-captioning-light.svg | 1 + .../public/light/cloud-arrow-down-light.svg | 1 + .../public/light/cloud-arrow-up-light.svg | 1 + frontend/public/light/cloud-check-light.svg | 1 + frontend/public/light/cloud-fog-light.svg | 1 + frontend/public/light/cloud-light.svg | 1 + .../public/light/cloud-lightning-light.svg | 1 + frontend/public/light/cloud-moon-light.svg | 1 + frontend/public/light/cloud-rain-light.svg | 1 + frontend/public/light/cloud-slash-light.svg | 1 + frontend/public/light/cloud-snow-light.svg | 1 + frontend/public/light/cloud-sun-light.svg | 1 + frontend/public/light/cloud-warning-light.svg | 1 + frontend/public/light/cloud-x-light.svg | 1 + frontend/public/light/clover-light.svg | 1 + frontend/public/light/club-light.svg | 1 + frontend/public/light/coat-hanger-light.svg | 1 + frontend/public/light/coda-logo-light.svg | 1 + frontend/public/light/code-block-light.svg | 1 + frontend/public/light/code-light.svg | 1 + frontend/public/light/code-simple-light.svg | 1 + frontend/public/light/codepen-logo-light.svg | 1 + .../public/light/codesandbox-logo-light.svg | 1 + frontend/public/light/coffee-bean-light.svg | 1 + frontend/public/light/coffee-light.svg | 1 + frontend/public/light/coin-light.svg | 1 + frontend/public/light/coin-vertical-light.svg | 1 + frontend/public/light/coins-light.svg | 1 + frontend/public/light/columns-light.svg | 1 + .../public/light/columns-plus-left-light.svg | 1 + .../public/light/columns-plus-right-light.svg | 1 + frontend/public/light/command-light.svg | 1 + frontend/public/light/compass-light.svg | 1 + frontend/public/light/compass-rose-light.svg | 1 + frontend/public/light/compass-tool-light.svg | 1 + .../public/light/computer-tower-light.svg | 1 + frontend/public/light/confetti-light.svg | 1 + .../light/contactless-payment-light.svg | 1 + frontend/public/light/control-light.svg | 1 + frontend/public/light/cookie-light.svg | 1 + frontend/public/light/cooking-pot-light.svg | 1 + frontend/public/light/copy-light.svg | 1 + frontend/public/light/copy-simple-light.svg | 1 + frontend/public/light/copyleft-light.svg | 1 + frontend/public/light/copyright-light.svg | 1 + frontend/public/light/corners-in-light.svg | 1 + frontend/public/light/corners-out-light.svg | 1 + frontend/public/light/couch-light.svg | 1 + .../public/light/court-basketball-light.svg | 1 + frontend/public/light/cow-light.svg | 1 + frontend/public/light/cowboy-hat-light.svg | 1 + frontend/public/light/cpu-light.svg | 1 + frontend/public/light/crane-light.svg | 1 + frontend/public/light/crane-tower-light.svg | 1 + frontend/public/light/credit-card-light.svg | 1 + frontend/public/light/cricket-light.svg | 1 + frontend/public/light/crop-light.svg | 1 + frontend/public/light/cross-light.svg | 1 + frontend/public/light/crosshair-light.svg | 1 + .../public/light/crosshair-simple-light.svg | 1 + frontend/public/light/crown-cross-light.svg | 1 + frontend/public/light/crown-light.svg | 1 + frontend/public/light/crown-simple-light.svg | 1 + frontend/public/light/cube-focus-light.svg | 1 + frontend/public/light/cube-light.svg | 1 + .../public/light/cube-transparent-light.svg | 1 + frontend/public/light/currency-btc-light.svg | 1 + .../light/currency-circle-dollar-light.svg | 1 + frontend/public/light/currency-cny-light.svg | 1 + .../public/light/currency-dollar-light.svg | 1 + .../light/currency-dollar-simple-light.svg | 1 + frontend/public/light/currency-eth-light.svg | 1 + frontend/public/light/currency-eur-light.svg | 1 + frontend/public/light/currency-gbp-light.svg | 1 + frontend/public/light/currency-inr-light.svg | 1 + frontend/public/light/currency-jpy-light.svg | 1 + frontend/public/light/currency-krw-light.svg | 1 + frontend/public/light/currency-kzt-light.svg | 1 + frontend/public/light/currency-ngn-light.svg | 1 + frontend/public/light/currency-rub-light.svg | 1 + frontend/public/light/cursor-click-light.svg | 1 + frontend/public/light/cursor-light.svg | 1 + frontend/public/light/cursor-text-light.svg | 1 + frontend/public/light/cylinder-light.svg | 1 + frontend/public/light/database-light.svg | 1 + frontend/public/light/desk-light.svg | 1 + frontend/public/light/desktop-light.svg | 1 + frontend/public/light/desktop-tower-light.svg | 1 + frontend/public/light/detective-light.svg | 1 + frontend/public/light/dev-to-logo-light.svg | 1 + .../light/device-mobile-camera-light.svg | 1 + frontend/public/light/device-mobile-light.svg | 1 + .../light/device-mobile-slash-light.svg | 1 + .../light/device-mobile-speaker-light.svg | 1 + frontend/public/light/device-rotate-light.svg | 1 + .../light/device-tablet-camera-light.svg | 1 + frontend/public/light/device-tablet-light.svg | 1 + .../light/device-tablet-speaker-light.svg | 1 + frontend/public/light/devices-light.svg | 1 + frontend/public/light/diamond-light.svg | 1 + frontend/public/light/diamonds-four-light.svg | 1 + frontend/public/light/dice-five-light.svg | 1 + frontend/public/light/dice-four-light.svg | 1 + frontend/public/light/dice-one-light.svg | 1 + frontend/public/light/dice-six-light.svg | 1 + frontend/public/light/dice-three-light.svg | 1 + frontend/public/light/dice-two-light.svg | 1 + frontend/public/light/disc-light.svg | 1 + frontend/public/light/disco-ball-light.svg | 1 + frontend/public/light/discord-logo-light.svg | 1 + frontend/public/light/divide-light.svg | 1 + frontend/public/light/dna-light.svg | 1 + frontend/public/light/dog-light.svg | 1 + frontend/public/light/door-light.svg | 1 + frontend/public/light/door-open-light.svg | 1 + frontend/public/light/dot-light.svg | 1 + frontend/public/light/dot-outline-light.svg | 1 + frontend/public/light/dots-nine-light.svg | 1 + frontend/public/light/dots-six-light.svg | 1 + .../public/light/dots-six-vertical-light.svg | 1 + .../public/light/dots-three-circle-light.svg | 1 + .../dots-three-circle-vertical-light.svg | 1 + frontend/public/light/dots-three-light.svg | 1 + .../public/light/dots-three-outline-light.svg | 1 + .../dots-three-outline-vertical-light.svg | 1 + .../light/dots-three-vertical-light.svg | 1 + frontend/public/light/download-light.svg | 1 + .../public/light/download-simple-light.svg | 1 + frontend/public/light/dress-light.svg | 1 + frontend/public/light/dresser-light.svg | 1 + frontend/public/light/dribbble-logo-light.svg | 1 + frontend/public/light/drone-light.svg | 1 + .../public/light/drop-half-bottom-light.svg | 1 + frontend/public/light/drop-half-light.svg | 1 + frontend/public/light/drop-light.svg | 1 + frontend/public/light/drop-simple-light.svg | 1 + frontend/public/light/drop-slash-light.svg | 1 + frontend/public/light/dropbox-logo-light.svg | 1 + frontend/public/light/ear-light.svg | 1 + frontend/public/light/ear-slash-light.svg | 1 + frontend/public/light/egg-crack-light.svg | 1 + frontend/public/light/egg-light.svg | 1 + frontend/public/light/eject-light.svg | 1 + frontend/public/light/eject-simple-light.svg | 1 + frontend/public/light/elevator-light.svg | 1 + frontend/public/light/empty-light.svg | 1 + frontend/public/light/engine-light.svg | 1 + frontend/public/light/envelope-light.svg | 1 + frontend/public/light/envelope-open-light.svg | 1 + .../public/light/envelope-simple-light.svg | 1 + .../light/envelope-simple-open-light.svg | 1 + frontend/public/light/equalizer-light.svg | 1 + frontend/public/light/equals-light.svg | 1 + frontend/public/light/eraser-light.svg | 1 + .../public/light/escalator-down-light.svg | 1 + frontend/public/light/escalator-up-light.svg | 1 + frontend/public/light/exam-light.svg | 1 + .../public/light/exclamation-mark-light.svg | 1 + frontend/public/light/exclude-light.svg | 1 + .../public/light/exclude-square-light.svg | 1 + frontend/public/light/export-light.svg | 1 + frontend/public/light/eye-closed-light.svg | 1 + frontend/public/light/eye-light.svg | 1 + frontend/public/light/eye-slash-light.svg | 1 + frontend/public/light/eyedropper-light.svg | 1 + .../public/light/eyedropper-sample-light.svg | 1 + frontend/public/light/eyeglasses-light.svg | 1 + frontend/public/light/eyes-light.svg | 1 + frontend/public/light/face-mask-light.svg | 1 + frontend/public/light/facebook-logo-light.svg | 1 + frontend/public/light/factory-light.svg | 1 + .../public/light/faders-horizontal-light.svg | 1 + frontend/public/light/faders-light.svg | 1 + .../public/light/fallout-shelter-light.svg | 1 + frontend/public/light/fan-light.svg | 1 + frontend/public/light/farm-light.svg | 1 + .../light/fast-forward-circle-light.svg | 1 + frontend/public/light/fast-forward-light.svg | 1 + frontend/public/light/feather-light.svg | 1 + .../public/light/fediverse-logo-light.svg | 1 + frontend/public/light/figma-logo-light.svg | 1 + frontend/public/light/file-archive-light.svg | 1 + .../public/light/file-arrow-down-light.svg | 1 + frontend/public/light/file-arrow-up-light.svg | 1 + frontend/public/light/file-audio-light.svg | 1 + frontend/public/light/file-c-light.svg | 1 + frontend/public/light/file-c-sharp-light.svg | 1 + frontend/public/light/file-cloud-light.svg | 1 + frontend/public/light/file-code-light.svg | 1 + frontend/public/light/file-cpp-light.svg | 1 + frontend/public/light/file-css-light.svg | 1 + frontend/public/light/file-csv-light.svg | 1 + frontend/public/light/file-dashed-light.svg | 1 + frontend/public/light/file-doc-light.svg | 1 + frontend/public/light/file-html-light.svg | 1 + frontend/public/light/file-image-light.svg | 1 + frontend/public/light/file-ini-light.svg | 1 + frontend/public/light/file-jpg-light.svg | 1 + frontend/public/light/file-js-light.svg | 1 + frontend/public/light/file-jsx-light.svg | 1 + frontend/public/light/file-light.svg | 1 + frontend/public/light/file-lock-light.svg | 1 + .../light/file-magnifying-glass-light.svg | 1 + frontend/public/light/file-md-light.svg | 1 + frontend/public/light/file-minus-light.svg | 1 + frontend/public/light/file-pdf-light.svg | 1 + frontend/public/light/file-plus-light.svg | 1 + frontend/public/light/file-png-light.svg | 1 + frontend/public/light/file-ppt-light.svg | 1 + frontend/public/light/file-py-light.svg | 1 + frontend/public/light/file-rs-light.svg | 1 + frontend/public/light/file-sql-light.svg | 1 + frontend/public/light/file-svg-light.svg | 1 + frontend/public/light/file-text-light.svg | 1 + frontend/public/light/file-ts-light.svg | 1 + frontend/public/light/file-tsx-light.svg | 1 + frontend/public/light/file-txt-light.svg | 1 + frontend/public/light/file-video-light.svg | 1 + frontend/public/light/file-vue-light.svg | 1 + frontend/public/light/file-x-light.svg | 1 + frontend/public/light/file-xls-light.svg | 1 + frontend/public/light/file-zip-light.svg | 1 + frontend/public/light/files-light.svg | 1 + frontend/public/light/film-reel-light.svg | 1 + frontend/public/light/film-script-light.svg | 1 + frontend/public/light/film-slate-light.svg | 1 + frontend/public/light/film-strip-light.svg | 1 + frontend/public/light/fingerprint-light.svg | 1 + .../public/light/fingerprint-simple-light.svg | 1 + .../public/light/finn-the-human-light.svg | 1 + .../public/light/fire-extinguisher-light.svg | 1 + frontend/public/light/fire-light.svg | 1 + frontend/public/light/fire-simple-light.svg | 1 + frontend/public/light/fire-truck-light.svg | 1 + frontend/public/light/first-aid-kit-light.svg | 1 + frontend/public/light/first-aid-light.svg | 1 + frontend/public/light/fish-light.svg | 1 + frontend/public/light/fish-simple-light.svg | 1 + .../public/light/flag-banner-fold-light.svg | 1 + frontend/public/light/flag-banner-light.svg | 1 + .../public/light/flag-checkered-light.svg | 1 + frontend/public/light/flag-light.svg | 1 + frontend/public/light/flag-pennant-light.svg | 1 + frontend/public/light/flame-light.svg | 1 + frontend/public/light/flashlight-light.svg | 1 + frontend/public/light/flask-light.svg | 1 + .../public/light/flip-horizontal-light.svg | 1 + frontend/public/light/flip-vertical-light.svg | 1 + .../public/light/floppy-disk-back-light.svg | 1 + frontend/public/light/floppy-disk-light.svg | 1 + frontend/public/light/flow-arrow-light.svg | 1 + frontend/public/light/flower-light.svg | 1 + frontend/public/light/flower-lotus-light.svg | 1 + frontend/public/light/flower-tulip-light.svg | 1 + frontend/public/light/flying-saucer-light.svg | 1 + frontend/public/light/folder-dashed-light.svg | 1 + frontend/public/light/folder-light.svg | 1 + frontend/public/light/folder-lock-light.svg | 1 + frontend/public/light/folder-minus-light.svg | 1 + frontend/public/light/folder-open-light.svg | 1 + frontend/public/light/folder-plus-light.svg | 1 + .../light/folder-simple-dashed-light.svg | 1 + frontend/public/light/folder-simple-light.svg | 1 + .../public/light/folder-simple-lock-light.svg | 1 + .../light/folder-simple-minus-light.svg | 1 + .../public/light/folder-simple-plus-light.svg | 1 + .../public/light/folder-simple-star-light.svg | 1 + .../public/light/folder-simple-user-light.svg | 1 + frontend/public/light/folder-star-light.svg | 1 + frontend/public/light/folder-user-light.svg | 1 + frontend/public/light/folders-light.svg | 1 + .../public/light/football-helmet-light.svg | 1 + frontend/public/light/football-light.svg | 1 + frontend/public/light/footprints-light.svg | 1 + frontend/public/light/fork-knife-light.svg | 1 + frontend/public/light/four-k-light.svg | 1 + frontend/public/light/frame-corners-light.svg | 1 + frontend/public/light/framer-logo-light.svg | 1 + frontend/public/light/function-light.svg | 1 + frontend/public/light/funnel-light.svg | 1 + frontend/public/light/funnel-simple-light.svg | 1 + .../public/light/funnel-simple-x-light.svg | 1 + frontend/public/light/funnel-x-light.svg | 1 + .../public/light/game-controller-light.svg | 1 + frontend/public/light/garage-light.svg | 1 + frontend/public/light/gas-can-light.svg | 1 + frontend/public/light/gas-pump-light.svg | 1 + frontend/public/light/gauge-light.svg | 1 + frontend/public/light/gavel-light.svg | 1 + frontend/public/light/gear-fine-light.svg | 1 + frontend/public/light/gear-light.svg | 1 + frontend/public/light/gear-six-light.svg | 1 + frontend/public/light/gender-female-light.svg | 1 + .../public/light/gender-intersex-light.svg | 1 + frontend/public/light/gender-male-light.svg | 1 + frontend/public/light/gender-neuter-light.svg | 1 + .../public/light/gender-nonbinary-light.svg | 1 + .../public/light/gender-transgender-light.svg | 1 + frontend/public/light/ghost-light.svg | 1 + frontend/public/light/gif-light.svg | 1 + frontend/public/light/gift-light.svg | 1 + frontend/public/light/git-branch-light.svg | 1 + frontend/public/light/git-commit-light.svg | 1 + frontend/public/light/git-diff-light.svg | 1 + frontend/public/light/git-fork-light.svg | 1 + frontend/public/light/git-merge-light.svg | 1 + .../public/light/git-pull-request-light.svg | 1 + frontend/public/light/github-logo-light.svg | 1 + frontend/public/light/gitlab-logo-light.svg | 1 + .../public/light/gitlab-logo-simple-light.svg | 1 + .../light/globe-hemisphere-east-light.svg | 1 + .../light/globe-hemisphere-west-light.svg | 1 + frontend/public/light/globe-light.svg | 1 + frontend/public/light/globe-simple-light.svg | 1 + .../public/light/globe-simple-x-light.svg | 1 + frontend/public/light/globe-stand-light.svg | 1 + frontend/public/light/globe-x-light.svg | 1 + frontend/public/light/goggles-light.svg | 1 + frontend/public/light/golf-light.svg | 1 + .../public/light/goodreads-logo-light.svg | 1 + .../light/google-cardboard-logo-light.svg | 1 + .../public/light/google-chrome-logo-light.svg | 1 + .../public/light/google-drive-logo-light.svg | 1 + frontend/public/light/google-logo-light.svg | 1 + .../public/light/google-photos-logo-light.svg | 1 + .../public/light/google-play-logo-light.svg | 1 + .../light/google-podcasts-logo-light.svg | 1 + frontend/public/light/gps-fix-light.svg | 1 + frontend/public/light/gps-light.svg | 1 + frontend/public/light/gps-slash-light.svg | 1 + frontend/public/light/gradient-light.svg | 1 + .../public/light/graduation-cap-light.svg | 1 + frontend/public/light/grains-light.svg | 1 + frontend/public/light/grains-slash-light.svg | 1 + frontend/public/light/graph-light.svg | 1 + frontend/public/light/graphics-card-light.svg | 1 + frontend/public/light/greater-than-light.svg | 1 + .../light/greater-than-or-equal-light.svg | 1 + frontend/public/light/grid-four-light.svg | 1 + frontend/public/light/grid-nine-light.svg | 1 + frontend/public/light/guitar-light.svg | 1 + frontend/public/light/hair-dryer-light.svg | 1 + frontend/public/light/hamburger-light.svg | 1 + frontend/public/light/hammer-light.svg | 1 + .../public/light/hand-arrow-down-light.svg | 1 + frontend/public/light/hand-arrow-up-light.svg | 1 + frontend/public/light/hand-coins-light.svg | 1 + frontend/public/light/hand-deposit-light.svg | 1 + frontend/public/light/hand-eye-light.svg | 1 + frontend/public/light/hand-fist-light.svg | 1 + frontend/public/light/hand-grabbing-light.svg | 1 + frontend/public/light/hand-heart-light.svg | 1 + frontend/public/light/hand-light.svg | 1 + frontend/public/light/hand-palm-light.svg | 1 + frontend/public/light/hand-peace-light.svg | 1 + frontend/public/light/hand-pointing-light.svg | 1 + frontend/public/light/hand-soap-light.svg | 1 + .../public/light/hand-swipe-left-light.svg | 1 + .../public/light/hand-swipe-right-light.svg | 1 + frontend/public/light/hand-tap-light.svg | 1 + frontend/public/light/hand-waving-light.svg | 1 + frontend/public/light/hand-withdraw-light.svg | 1 + frontend/public/light/handbag-light.svg | 1 + .../public/light/handbag-simple-light.svg | 1 + .../public/light/hands-clapping-light.svg | 1 + frontend/public/light/hands-praying-light.svg | 1 + frontend/public/light/handshake-light.svg | 1 + frontend/public/light/hard-drive-light.svg | 1 + frontend/public/light/hard-drives-light.svg | 1 + frontend/public/light/hard-hat-light.svg | 1 + frontend/public/light/hash-light.svg | 1 + frontend/public/light/hash-straight-light.svg | 1 + frontend/public/light/head-circuit-light.svg | 1 + frontend/public/light/headlights-light.svg | 1 + frontend/public/light/headphones-light.svg | 1 + frontend/public/light/headset-light.svg | 1 + frontend/public/light/heart-break-light.svg | 1 + frontend/public/light/heart-half-light.svg | 1 + frontend/public/light/heart-light.svg | 1 + .../light/heart-straight-break-light.svg | 1 + .../public/light/heart-straight-light.svg | 1 + frontend/public/light/heartbeat-light.svg | 1 + frontend/public/light/hexagon-light.svg | 1 + .../public/light/high-definition-light.svg | 1 + frontend/public/light/high-heel-light.svg | 1 + .../public/light/highlighter-circle-light.svg | 1 + frontend/public/light/highlighter-light.svg | 1 + frontend/public/light/hockey-light.svg | 1 + frontend/public/light/hoodie-light.svg | 1 + frontend/public/light/horse-light.svg | 1 + frontend/public/light/hospital-light.svg | 1 + .../public/light/hourglass-high-light.svg | 1 + frontend/public/light/hourglass-light.svg | 1 + frontend/public/light/hourglass-low-light.svg | 1 + .../public/light/hourglass-medium-light.svg | 1 + .../light/hourglass-simple-high-light.svg | 1 + .../public/light/hourglass-simple-light.svg | 1 + .../light/hourglass-simple-low-light.svg | 1 + .../light/hourglass-simple-medium-light.svg | 1 + frontend/public/light/house-light.svg | 1 + frontend/public/light/house-line-light.svg | 1 + frontend/public/light/house-simple-light.svg | 1 + frontend/public/light/hurricane-light.svg | 1 + frontend/public/light/ice-cream-light.svg | 1 + .../light/identification-badge-light.svg | 1 + .../light/identification-card-light.svg | 1 + frontend/public/light/image-broken-light.svg | 1 + frontend/public/light/image-light.svg | 1 + frontend/public/light/image-square-light.svg | 1 + frontend/public/light/images-light.svg | 1 + frontend/public/light/images-square-light.svg | 1 + frontend/public/light/infinity-light.svg | 1 + frontend/public/light/info-light.svg | 1 + .../public/light/instagram-logo-light.svg | 1 + frontend/public/light/intersect-light.svg | 1 + .../public/light/intersect-square-light.svg | 1 + .../public/light/intersect-three-light.svg | 1 + frontend/public/light/intersection-light.svg | 1 + frontend/public/light/invoice-light.svg | 1 + frontend/public/light/island-light.svg | 1 + frontend/public/light/jar-label-light.svg | 1 + frontend/public/light/jar-light.svg | 1 + frontend/public/light/jeep-light.svg | 1 + frontend/public/light/joystick-light.svg | 1 + frontend/public/light/kanban-light.svg | 1 + frontend/public/light/key-light.svg | 1 + frontend/public/light/key-return-light.svg | 1 + frontend/public/light/keyboard-light.svg | 1 + frontend/public/light/keyhole-light.svg | 1 + frontend/public/light/knife-light.svg | 1 + frontend/public/light/ladder-light.svg | 1 + frontend/public/light/ladder-simple-light.svg | 1 + frontend/public/light/lamp-light.svg | 1 + frontend/public/light/lamp-pendant-light.svg | 1 + frontend/public/light/laptop-light.svg | 1 + frontend/public/light/lasso-light.svg | 1 + frontend/public/light/lastfm-logo-light.svg | 1 + frontend/public/light/layout-light.svg | 1 + frontend/public/light/leaf-light.svg | 1 + frontend/public/light/lectern-light.svg | 1 + frontend/public/light/lego-light.svg | 1 + frontend/public/light/lego-smiley-light.svg | 1 + frontend/public/light/less-than-light.svg | 1 + .../public/light/less-than-or-equal-light.svg | 1 + .../public/light/letter-circle-h-light.svg | 1 + .../public/light/letter-circle-p-light.svg | 1 + .../public/light/letter-circle-v-light.svg | 1 + frontend/public/light/lifebuoy-light.svg | 1 + .../public/light/lightbulb-filament-light.svg | 1 + frontend/public/light/lightbulb-light.svg | 1 + frontend/public/light/lighthouse-light.svg | 1 + frontend/public/light/lightning-a-light.svg | 1 + frontend/public/light/lightning-light.svg | 1 + .../public/light/lightning-slash-light.svg | 1 + frontend/public/light/line-segment-light.svg | 1 + frontend/public/light/line-segments-light.svg | 1 + frontend/public/light/line-vertical-light.svg | 1 + frontend/public/light/link-break-light.svg | 1 + frontend/public/light/link-light.svg | 1 + .../public/light/link-simple-break-light.svg | 1 + .../link-simple-horizontal-break-light.svg | 1 + .../light/link-simple-horizontal-light.svg | 1 + frontend/public/light/link-simple-light.svg | 1 + frontend/public/light/linkedin-logo-light.svg | 1 + frontend/public/light/linktree-logo-light.svg | 1 + frontend/public/light/linux-logo-light.svg | 1 + frontend/public/light/list-bullets-light.svg | 1 + frontend/public/light/list-checks-light.svg | 1 + frontend/public/light/list-dashes-light.svg | 1 + frontend/public/light/list-heart-light.svg | 1 + frontend/public/light/list-light.svg | 1 + .../light/list-magnifying-glass-light.svg | 1 + frontend/public/light/list-numbers-light.svg | 1 + frontend/public/light/list-plus-light.svg | 1 + frontend/public/light/list-star-light.svg | 1 + frontend/public/light/lock-key-light.svg | 1 + frontend/public/light/lock-key-open-light.svg | 1 + .../public/light/lock-laminated-light.svg | 1 + .../light/lock-laminated-open-light.svg | 1 + frontend/public/light/lock-light.svg | 1 + frontend/public/light/lock-open-light.svg | 1 + frontend/public/light/lock-simple-light.svg | 1 + .../public/light/lock-simple-open-light.svg | 1 + frontend/public/light/lockers-light.svg | 1 + frontend/public/light/log-light.svg | 1 + frontend/public/light/magic-wand-light.svg | 1 + frontend/public/light/magnet-light.svg | 1 + .../public/light/magnet-straight-light.svg | 1 + .../public/light/magnifying-glass-light.svg | 1 + .../light/magnifying-glass-minus-light.svg | 1 + .../light/magnifying-glass-plus-light.svg | 1 + frontend/public/light/mailbox-light.svg | 1 + frontend/public/light/map-pin-area-light.svg | 1 + frontend/public/light/map-pin-light.svg | 1 + frontend/public/light/map-pin-line-light.svg | 1 + frontend/public/light/map-pin-plus-light.svg | 1 + .../light/map-pin-simple-area-light.svg | 1 + .../public/light/map-pin-simple-light.svg | 1 + .../light/map-pin-simple-line-light.svg | 1 + frontend/public/light/map-trifold-light.svg | 1 + frontend/public/light/markdown-logo-light.svg | 1 + frontend/public/light/marker-circle-light.svg | 1 + frontend/public/light/martini-light.svg | 1 + frontend/public/light/mask-happy-light.svg | 1 + frontend/public/light/mask-sad-light.svg | 1 + frontend/public/light/mastodon-logo-light.svg | 1 + .../public/light/math-operations-light.svg | 1 + frontend/public/light/matrix-logo-light.svg | 1 + frontend/public/light/medal-light.svg | 1 + .../public/light/medal-military-light.svg | 1 + frontend/public/light/medium-logo-light.svg | 1 + frontend/public/light/megaphone-light.svg | 1 + .../public/light/megaphone-simple-light.svg | 1 + frontend/public/light/member-of-light.svg | 1 + frontend/public/light/memory-light.svg | 1 + .../public/light/messenger-logo-light.svg | 1 + frontend/public/light/meta-logo-light.svg | 1 + frontend/public/light/meteor-light.svg | 1 + frontend/public/light/metronome-light.svg | 1 + frontend/public/light/microphone-light.svg | 1 + .../public/light/microphone-slash-light.svg | 1 + .../public/light/microphone-stage-light.svg | 1 + frontend/public/light/microscope-light.svg | 1 + .../light/microsoft-excel-logo-light.svg | 1 + .../light/microsoft-outlook-logo-light.svg | 1 + .../light/microsoft-powerpoint-logo-light.svg | 1 + .../light/microsoft-teams-logo-light.svg | 1 + .../light/microsoft-word-logo-light.svg | 1 + frontend/public/light/minus-circle-light.svg | 1 + frontend/public/light/minus-light.svg | 1 + frontend/public/light/minus-square-light.svg | 1 + frontend/public/light/money-light.svg | 1 + frontend/public/light/money-wavy-light.svg | 1 + .../public/light/monitor-arrow-up-light.svg | 1 + frontend/public/light/monitor-light.svg | 1 + frontend/public/light/monitor-play-light.svg | 1 + frontend/public/light/moon-light.svg | 1 + frontend/public/light/moon-stars-light.svg | 1 + frontend/public/light/moped-front-light.svg | 1 + frontend/public/light/moped-light.svg | 1 + frontend/public/light/mosque-light.svg | 1 + frontend/public/light/motorcycle-light.svg | 1 + frontend/public/light/mountains-light.svg | 1 + .../public/light/mouse-left-click-light.svg | 1 + frontend/public/light/mouse-light.svg | 1 + .../public/light/mouse-middle-click-light.svg | 1 + .../public/light/mouse-right-click-light.svg | 1 + frontend/public/light/mouse-scroll-light.svg | 1 + frontend/public/light/mouse-simple-light.svg | 1 + frontend/public/light/music-note-light.svg | 1 + .../public/light/music-note-simple-light.svg | 1 + frontend/public/light/music-notes-light.svg | 1 + .../public/light/music-notes-minus-light.svg | 1 + .../public/light/music-notes-plus-light.svg | 1 + .../public/light/music-notes-simple-light.svg | 1 + .../public/light/navigation-arrow-light.svg | 1 + frontend/public/light/needle-light.svg | 1 + frontend/public/light/network-light.svg | 1 + frontend/public/light/network-slash-light.svg | 1 + frontend/public/light/network-x-light.svg | 1 + .../public/light/newspaper-clipping-light.svg | 1 + frontend/public/light/newspaper-light.svg | 1 + frontend/public/light/not-equals-light.svg | 1 + frontend/public/light/not-member-of-light.svg | 1 + frontend/public/light/not-subset-of-light.svg | 1 + .../public/light/not-superset-of-light.svg | 1 + frontend/public/light/notches-light.svg | 1 + frontend/public/light/note-blank-light.svg | 1 + frontend/public/light/note-light.svg | 1 + frontend/public/light/note-pencil-light.svg | 1 + frontend/public/light/notebook-light.svg | 1 + frontend/public/light/notepad-light.svg | 1 + frontend/public/light/notification-light.svg | 1 + frontend/public/light/notion-logo-light.svg | 1 + frontend/public/light/nuclear-plant-light.svg | 1 + .../light/number-circle-eight-light.svg | 1 + .../public/light/number-circle-five-light.svg | 1 + .../public/light/number-circle-four-light.svg | 1 + .../public/light/number-circle-nine-light.svg | 1 + .../public/light/number-circle-one-light.svg | 1 + .../light/number-circle-seven-light.svg | 1 + .../public/light/number-circle-six-light.svg | 1 + .../light/number-circle-three-light.svg | 1 + .../public/light/number-circle-two-light.svg | 1 + .../public/light/number-circle-zero-light.svg | 1 + frontend/public/light/number-eight-light.svg | 1 + frontend/public/light/number-five-light.svg | 1 + frontend/public/light/number-four-light.svg | 1 + frontend/public/light/number-nine-light.svg | 1 + frontend/public/light/number-one-light.svg | 1 + frontend/public/light/number-seven-light.svg | 1 + frontend/public/light/number-six-light.svg | 1 + .../light/number-square-eight-light.svg | 1 + .../public/light/number-square-five-light.svg | 1 + .../public/light/number-square-four-light.svg | 1 + .../public/light/number-square-nine-light.svg | 1 + .../public/light/number-square-one-light.svg | 1 + .../light/number-square-seven-light.svg | 1 + .../public/light/number-square-six-light.svg | 1 + .../light/number-square-three-light.svg | 1 + .../public/light/number-square-two-light.svg | 1 + .../public/light/number-square-zero-light.svg | 1 + frontend/public/light/number-three-light.svg | 1 + frontend/public/light/number-two-light.svg | 1 + frontend/public/light/number-zero-light.svg | 1 + frontend/public/light/numpad-light.svg | 1 + frontend/public/light/nut-light.svg | 1 + frontend/public/light/ny-times-logo-light.svg | 1 + frontend/public/light/octagon-light.svg | 1 + frontend/public/light/office-chair-light.svg | 1 + frontend/public/light/onigiri-light.svg | 1 + frontend/public/light/open-ai-logo-light.svg | 1 + frontend/public/light/option-light.svg | 1 + frontend/public/light/orange-light.svg | 1 + frontend/public/light/orange-slice-light.svg | 1 + frontend/public/light/oven-light.svg | 1 + frontend/public/light/package-light.svg | 1 + .../public/light/paint-brush-broad-light.svg | 1 + .../light/paint-brush-household-light.svg | 1 + frontend/public/light/paint-brush-light.svg | 1 + frontend/public/light/paint-bucket-light.svg | 1 + frontend/public/light/paint-roller-light.svg | 1 + frontend/public/light/palette-light.svg | 1 + frontend/public/light/panorama-light.svg | 1 + frontend/public/light/pants-light.svg | 1 + frontend/public/light/paper-plane-light.svg | 1 + .../public/light/paper-plane-right-light.svg | 1 + .../public/light/paper-plane-tilt-light.svg | 1 + .../light/paperclip-horizontal-light.svg | 1 + frontend/public/light/paperclip-light.svg | 1 + frontend/public/light/parachute-light.svg | 1 + frontend/public/light/paragraph-light.svg | 1 + frontend/public/light/parallelogram-light.svg | 1 + frontend/public/light/park-light.svg | 1 + frontend/public/light/password-light.svg | 1 + frontend/public/light/path-light.svg | 1 + frontend/public/light/patreon-logo-light.svg | 1 + frontend/public/light/pause-circle-light.svg | 1 + frontend/public/light/pause-light.svg | 1 + frontend/public/light/paw-print-light.svg | 1 + frontend/public/light/paypal-logo-light.svg | 1 + frontend/public/light/peace-light.svg | 1 + frontend/public/light/pen-light.svg | 1 + frontend/public/light/pen-nib-light.svg | 1 + .../public/light/pen-nib-straight-light.svg | 1 + frontend/public/light/pencil-circle-light.svg | 1 + frontend/public/light/pencil-light.svg | 1 + frontend/public/light/pencil-line-light.svg | 1 + frontend/public/light/pencil-ruler-light.svg | 1 + frontend/public/light/pencil-simple-light.svg | 1 + .../public/light/pencil-simple-line-light.svg | 1 + .../light/pencil-simple-slash-light.svg | 1 + frontend/public/light/pencil-slash-light.svg | 1 + frontend/public/light/pentagon-light.svg | 1 + frontend/public/light/pentagram-light.svg | 1 + frontend/public/light/pepper-light.svg | 1 + frontend/public/light/percent-light.svg | 1 + .../public/light/person-arms-spread-light.svg | 1 + frontend/public/light/person-light.svg | 1 + .../public/light/person-simple-bike-light.svg | 1 + .../light/person-simple-circle-light.svg | 1 + .../public/light/person-simple-hike-light.svg | 1 + frontend/public/light/person-simple-light.svg | 1 + .../public/light/person-simple-run-light.svg | 1 + .../public/light/person-simple-ski-light.svg | 1 + .../light/person-simple-snowboard-light.svg | 1 + .../public/light/person-simple-swim-light.svg | 1 + .../light/person-simple-tai-chi-light.svg | 1 + .../light/person-simple-throw-light.svg | 1 + .../public/light/person-simple-walk-light.svg | 1 + frontend/public/light/perspective-light.svg | 1 + frontend/public/light/phone-call-light.svg | 1 + .../public/light/phone-disconnect-light.svg | 1 + .../public/light/phone-incoming-light.svg | 1 + frontend/public/light/phone-light.svg | 1 + frontend/public/light/phone-list-light.svg | 1 + .../public/light/phone-outgoing-light.svg | 1 + frontend/public/light/phone-pause-light.svg | 1 + frontend/public/light/phone-plus-light.svg | 1 + frontend/public/light/phone-slash-light.svg | 1 + .../public/light/phone-transfer-light.svg | 1 + frontend/public/light/phone-x-light.svg | 1 + frontend/public/light/phosphor-logo-light.svg | 1 + frontend/public/light/pi-light.svg | 1 + frontend/public/light/piano-keys-light.svg | 1 + frontend/public/light/picnic-table-light.svg | 1 + .../public/light/picture-in-picture-light.svg | 1 + frontend/public/light/piggy-bank-light.svg | 1 + frontend/public/light/pill-light.svg | 1 + frontend/public/light/ping-pong-light.svg | 1 + frontend/public/light/pint-glass-light.svg | 1 + .../public/light/pinterest-logo-light.svg | 1 + frontend/public/light/pinwheel-light.svg | 1 + frontend/public/light/pipe-light.svg | 1 + frontend/public/light/pipe-wrench-light.svg | 1 + frontend/public/light/pix-logo-light.svg | 1 + frontend/public/light/pizza-light.svg | 1 + frontend/public/light/placeholder-light.svg | 1 + frontend/public/light/planet-light.svg | 1 + frontend/public/light/plant-light.svg | 1 + frontend/public/light/play-circle-light.svg | 1 + frontend/public/light/play-light.svg | 1 + frontend/public/light/play-pause-light.svg | 1 + frontend/public/light/playlist-light.svg | 1 + frontend/public/light/plug-charging-light.svg | 1 + frontend/public/light/plug-light.svg | 1 + .../public/light/plugs-connected-light.svg | 1 + frontend/public/light/plugs-light.svg | 1 + frontend/public/light/plus-circle-light.svg | 1 + frontend/public/light/plus-light.svg | 1 + frontend/public/light/plus-minus-light.svg | 1 + frontend/public/light/plus-square-light.svg | 1 + frontend/public/light/poker-chip-light.svg | 1 + frontend/public/light/police-car-light.svg | 1 + frontend/public/light/polygon-light.svg | 1 + frontend/public/light/popcorn-light.svg | 1 + frontend/public/light/popsicle-light.svg | 1 + frontend/public/light/potted-plant-light.svg | 1 + frontend/public/light/power-light.svg | 1 + frontend/public/light/prescription-light.svg | 1 + .../public/light/presentation-chart-light.svg | 1 + frontend/public/light/presentation-light.svg | 1 + frontend/public/light/printer-light.svg | 1 + .../public/light/prohibit-inset-light.svg | 1 + frontend/public/light/prohibit-light.svg | 1 + .../light/projector-screen-chart-light.svg | 1 + .../public/light/projector-screen-light.svg | 1 + frontend/public/light/pulse-light.svg | 1 + frontend/public/light/push-pin-light.svg | 1 + .../public/light/push-pin-simple-light.svg | 1 + .../light/push-pin-simple-slash-light.svg | 1 + .../public/light/push-pin-slash-light.svg | 1 + frontend/public/light/puzzle-piece-light.svg | 1 + frontend/public/light/qr-code-light.svg | 1 + frontend/public/light/question-light.svg | 1 + frontend/public/light/question-mark-light.svg | 1 + frontend/public/light/queue-light.svg | 1 + frontend/public/light/quotes-light.svg | 1 + frontend/public/light/rabbit-light.svg | 1 + frontend/public/light/racquet-light.svg | 1 + frontend/public/light/radical-light.svg | 1 + frontend/public/light/radio-button-light.svg | 1 + frontend/public/light/radio-light.svg | 1 + frontend/public/light/radioactive-light.svg | 1 + frontend/public/light/rainbow-cloud-light.svg | 1 + frontend/public/light/rainbow-light.svg | 1 + frontend/public/light/ranking-light.svg | 1 + frontend/public/light/read-cv-logo-light.svg | 1 + frontend/public/light/receipt-light.svg | 1 + frontend/public/light/receipt-x-light.svg | 1 + frontend/public/light/record-light.svg | 1 + .../public/light/rectangle-dashed-light.svg | 1 + frontend/public/light/rectangle-light.svg | 1 + frontend/public/light/recycle-light.svg | 1 + frontend/public/light/reddit-logo-light.svg | 1 + frontend/public/light/repeat-light.svg | 1 + frontend/public/light/repeat-once-light.svg | 1 + frontend/public/light/replit-logo-light.svg | 1 + frontend/public/light/resize-light.svg | 1 + frontend/public/light/rewind-circle-light.svg | 1 + frontend/public/light/rewind-light.svg | 1 + frontend/public/light/road-horizon-light.svg | 1 + frontend/public/light/robot-light.svg | 1 + frontend/public/light/rocket-launch-light.svg | 1 + frontend/public/light/rocket-light.svg | 1 + frontend/public/light/rows-light.svg | 1 + .../public/light/rows-plus-bottom-light.svg | 1 + frontend/public/light/rows-plus-top-light.svg | 1 + frontend/public/light/rss-light.svg | 1 + frontend/public/light/rss-simple-light.svg | 1 + frontend/public/light/rug-light.svg | 1 + frontend/public/light/ruler-light.svg | 1 + frontend/public/light/sailboat-light.svg | 1 + frontend/public/light/scales-light.svg | 1 + frontend/public/light/scan-light.svg | 1 + frontend/public/light/scan-smiley-light.svg | 1 + frontend/public/light/scissors-light.svg | 1 + frontend/public/light/scooter-light.svg | 1 + frontend/public/light/screencast-light.svg | 1 + frontend/public/light/screwdriver-light.svg | 1 + frontend/public/light/scribble-light.svg | 1 + frontend/public/light/scribble-loop-light.svg | 1 + frontend/public/light/scroll-light.svg | 1 + frontend/public/light/seal-check-light.svg | 1 + frontend/public/light/seal-light.svg | 1 + frontend/public/light/seal-percent-light.svg | 1 + frontend/public/light/seal-question-light.svg | 1 + frontend/public/light/seal-warning-light.svg | 1 + frontend/public/light/seat-light.svg | 1 + frontend/public/light/seatbelt-light.svg | 1 + .../public/light/security-camera-light.svg | 1 + frontend/public/light/selection-all-light.svg | 1 + .../light/selection-background-light.svg | 1 + .../light/selection-foreground-light.svg | 1 + .../public/light/selection-inverse-light.svg | 1 + frontend/public/light/selection-light.svg | 1 + .../public/light/selection-plus-light.svg | 1 + .../public/light/selection-slash-light.svg | 1 + frontend/public/light/shapes-light.svg | 1 + frontend/public/light/share-fat-light.svg | 1 + frontend/public/light/share-light.svg | 1 + frontend/public/light/share-network-light.svg | 1 + frontend/public/light/shield-check-light.svg | 1 + .../public/light/shield-checkered-light.svg | 1 + .../public/light/shield-chevron-light.svg | 1 + frontend/public/light/shield-light.svg | 1 + frontend/public/light/shield-plus-light.svg | 1 + frontend/public/light/shield-slash-light.svg | 1 + frontend/public/light/shield-star-light.svg | 1 + .../public/light/shield-warning-light.svg | 1 + .../public/light/shipping-container-light.svg | 1 + frontend/public/light/shirt-folded-light.svg | 1 + frontend/public/light/shooting-star-light.svg | 1 + frontend/public/light/shopping-bag-light.svg | 1 + .../public/light/shopping-bag-open-light.svg | 1 + frontend/public/light/shopping-cart-light.svg | 1 + .../light/shopping-cart-simple-light.svg | 1 + frontend/public/light/shovel-light.svg | 1 + frontend/public/light/shower-light.svg | 1 + frontend/public/light/shrimp-light.svg | 1 + .../public/light/shuffle-angular-light.svg | 1 + frontend/public/light/shuffle-light.svg | 1 + .../public/light/shuffle-simple-light.svg | 1 + frontend/public/light/sidebar-light.svg | 1 + .../public/light/sidebar-simple-light.svg | 1 + frontend/public/light/sigma-light.svg | 1 + frontend/public/light/sign-in-light.svg | 1 + frontend/public/light/sign-out-light.svg | 1 + frontend/public/light/signature-light.svg | 1 + frontend/public/light/signpost-light.svg | 1 + frontend/public/light/sim-card-light.svg | 1 + frontend/public/light/siren-light.svg | 1 + frontend/public/light/sketch-logo-light.svg | 1 + .../public/light/skip-back-circle-light.svg | 1 + frontend/public/light/skip-back-light.svg | 1 + .../light/skip-forward-circle-light.svg | 1 + frontend/public/light/skip-forward-light.svg | 1 + frontend/public/light/skull-light.svg | 1 + frontend/public/light/skype-logo-light.svg | 1 + frontend/public/light/slack-logo-light.svg | 1 + .../public/light/sliders-horizontal-light.svg | 1 + frontend/public/light/sliders-light.svg | 1 + frontend/public/light/slideshow-light.svg | 1 + frontend/public/light/smiley-angry-light.svg | 1 + frontend/public/light/smiley-blank-light.svg | 1 + frontend/public/light/smiley-light.svg | 1 + frontend/public/light/smiley-meh-light.svg | 1 + .../public/light/smiley-melting-light.svg | 1 + .../public/light/smiley-nervous-light.svg | 1 + frontend/public/light/smiley-sad-light.svg | 1 + .../public/light/smiley-sticker-light.svg | 1 + frontend/public/light/smiley-wink-light.svg | 1 + frontend/public/light/smiley-x-eyes-light.svg | 1 + frontend/public/light/snapchat-logo-light.svg | 1 + frontend/public/light/sneaker-light.svg | 1 + frontend/public/light/sneaker-move-light.svg | 1 + frontend/public/light/snowflake-light.svg | 1 + frontend/public/light/soccer-ball-light.svg | 1 + frontend/public/light/sock-light.svg | 1 + frontend/public/light/solar-panel-light.svg | 1 + frontend/public/light/solar-roof-light.svg | 1 + .../public/light/sort-ascending-light.svg | 1 + .../public/light/sort-descending-light.svg | 1 + .../public/light/soundcloud-logo-light.svg | 1 + frontend/public/light/spade-light.svg | 1 + frontend/public/light/sparkle-light.svg | 1 + frontend/public/light/speaker-hifi-light.svg | 1 + frontend/public/light/speaker-high-light.svg | 1 + frontend/public/light/speaker-low-light.svg | 1 + frontend/public/light/speaker-none-light.svg | 1 + .../light/speaker-simple-high-light.svg | 1 + .../public/light/speaker-simple-low-light.svg | 1 + .../light/speaker-simple-none-light.svg | 1 + .../light/speaker-simple-slash-light.svg | 1 + .../public/light/speaker-simple-x-light.svg | 1 + frontend/public/light/speaker-slash-light.svg | 1 + frontend/public/light/speaker-x-light.svg | 1 + frontend/public/light/speedometer-light.svg | 1 + frontend/public/light/sphere-light.svg | 1 + frontend/public/light/spinner-ball-light.svg | 1 + frontend/public/light/spinner-gap-light.svg | 1 + frontend/public/light/spinner-light.svg | 1 + frontend/public/light/spiral-light.svg | 1 + .../public/light/split-horizontal-light.svg | 1 + .../public/light/split-vertical-light.svg | 1 + frontend/public/light/spotify-logo-light.svg | 1 + frontend/public/light/spray-bottle-light.svg | 1 + .../public/light/square-half-bottom-light.svg | 1 + frontend/public/light/square-half-light.svg | 1 + frontend/public/light/square-light.svg | 1 + frontend/public/light/square-logo-light.svg | 1 + .../light/square-split-horizontal-light.svg | 1 + .../light/square-split-vertical-light.svg | 1 + frontend/public/light/squares-four-light.svg | 1 + frontend/public/light/stack-light.svg | 1 + frontend/public/light/stack-minus-light.svg | 1 + .../light/stack-overflow-logo-light.svg | 1 + frontend/public/light/stack-plus-light.svg | 1 + frontend/public/light/stack-simple-light.svg | 1 + frontend/public/light/stairs-light.svg | 1 + frontend/public/light/stamp-light.svg | 1 + .../light/standard-definition-light.svg | 1 + .../public/light/star-and-crescent-light.svg | 1 + frontend/public/light/star-four-light.svg | 1 + frontend/public/light/star-half-light.svg | 1 + frontend/public/light/star-light.svg | 1 + frontend/public/light/star-of-david-light.svg | 1 + frontend/public/light/steam-logo-light.svg | 1 + .../public/light/steering-wheel-light.svg | 1 + frontend/public/light/steps-light.svg | 1 + frontend/public/light/stethoscope-light.svg | 1 + frontend/public/light/sticker-light.svg | 1 + frontend/public/light/stool-light.svg | 1 + frontend/public/light/stop-circle-light.svg | 1 + frontend/public/light/stop-light.svg | 1 + frontend/public/light/storefront-light.svg | 1 + frontend/public/light/strategy-light.svg | 1 + frontend/public/light/stripe-logo-light.svg | 1 + frontend/public/light/student-light.svg | 1 + frontend/public/light/subset-of-light.svg | 1 + .../public/light/subset-proper-of-light.svg | 1 + frontend/public/light/subtitles-light.svg | 1 + .../public/light/subtitles-slash-light.svg | 1 + frontend/public/light/subtract-light.svg | 1 + .../public/light/subtract-square-light.svg | 1 + frontend/public/light/subway-light.svg | 1 + frontend/public/light/suitcase-light.svg | 1 + .../public/light/suitcase-rolling-light.svg | 1 + .../public/light/suitcase-simple-light.svg | 1 + frontend/public/light/sun-dim-light.svg | 1 + frontend/public/light/sun-horizon-light.svg | 1 + frontend/public/light/sun-light.svg | 1 + frontend/public/light/sunglasses-light.svg | 1 + frontend/public/light/superset-of-light.svg | 1 + .../public/light/superset-proper-of-light.svg | 1 + frontend/public/light/swap-light.svg | 1 + frontend/public/light/swatches-light.svg | 1 + frontend/public/light/swimming-pool-light.svg | 1 + frontend/public/light/sword-light.svg | 1 + frontend/public/light/synagogue-light.svg | 1 + frontend/public/light/syringe-light.svg | 1 + frontend/public/light/t-shirt-light.svg | 1 + frontend/public/light/table-light.svg | 1 + frontend/public/light/tabs-light.svg | 1 + frontend/public/light/tag-chevron-light.svg | 1 + frontend/public/light/tag-light.svg | 1 + frontend/public/light/tag-simple-light.svg | 1 + frontend/public/light/target-light.svg | 1 + frontend/public/light/taxi-light.svg | 1 + frontend/public/light/tea-bag-light.svg | 1 + frontend/public/light/telegram-logo-light.svg | 1 + frontend/public/light/television-light.svg | 1 + .../public/light/television-simple-light.svg | 1 + frontend/public/light/tennis-ball-light.svg | 1 + frontend/public/light/tent-light.svg | 1 + frontend/public/light/terminal-light.svg | 1 + .../public/light/terminal-window-light.svg | 1 + frontend/public/light/test-tube-light.svg | 1 + .../public/light/text-a-underline-light.svg | 1 + frontend/public/light/text-aa-light.svg | 1 + .../public/light/text-align-center-light.svg | 1 + .../public/light/text-align-justify-light.svg | 1 + .../public/light/text-align-left-light.svg | 1 + .../public/light/text-align-right-light.svg | 1 + frontend/public/light/text-b-light.svg | 1 + frontend/public/light/text-columns-light.svg | 1 + frontend/public/light/text-h-five-light.svg | 1 + frontend/public/light/text-h-four-light.svg | 1 + frontend/public/light/text-h-light.svg | 1 + frontend/public/light/text-h-one-light.svg | 1 + frontend/public/light/text-h-six-light.svg | 1 + frontend/public/light/text-h-three-light.svg | 1 + frontend/public/light/text-h-two-light.svg | 1 + frontend/public/light/text-indent-light.svg | 1 + frontend/public/light/text-italic-light.svg | 1 + frontend/public/light/text-outdent-light.svg | 1 + .../public/light/text-strikethrough-light.svg | 1 + .../public/light/text-subscript-light.svg | 1 + .../public/light/text-superscript-light.svg | 1 + frontend/public/light/text-t-light.svg | 1 + frontend/public/light/text-t-slash-light.svg | 1 + .../public/light/text-underline-light.svg | 1 + frontend/public/light/textbox-light.svg | 1 + .../public/light/thermometer-cold-light.svg | 1 + .../public/light/thermometer-hot-light.svg | 1 + frontend/public/light/thermometer-light.svg | 1 + .../public/light/thermometer-simple-light.svg | 1 + frontend/public/light/threads-logo-light.svg | 1 + frontend/public/light/three-d-light.svg | 1 + frontend/public/light/thumbs-down-light.svg | 1 + frontend/public/light/thumbs-up-light.svg | 1 + frontend/public/light/ticket-light.svg | 1 + frontend/public/light/tidal-logo-light.svg | 1 + frontend/public/light/tiktok-logo-light.svg | 1 + frontend/public/light/tilde-light.svg | 1 + frontend/public/light/timer-light.svg | 1 + frontend/public/light/tip-jar-light.svg | 1 + frontend/public/light/tipi-light.svg | 1 + frontend/public/light/tire-light.svg | 1 + frontend/public/light/toggle-left-light.svg | 1 + frontend/public/light/toggle-right-light.svg | 1 + frontend/public/light/toilet-light.svg | 1 + frontend/public/light/toilet-paper-light.svg | 1 + frontend/public/light/toolbox-light.svg | 1 + frontend/public/light/tooth-light.svg | 1 + frontend/public/light/tornado-light.svg | 1 + frontend/public/light/tote-light.svg | 1 + frontend/public/light/tote-simple-light.svg | 1 + frontend/public/light/towel-light.svg | 1 + frontend/public/light/tractor-light.svg | 1 + frontend/public/light/trademark-light.svg | 1 + .../light/trademark-registered-light.svg | 1 + frontend/public/light/traffic-cone-light.svg | 1 + frontend/public/light/traffic-sign-light.svg | 1 + .../public/light/traffic-signal-light.svg | 1 + frontend/public/light/train-light.svg | 1 + .../public/light/train-regional-light.svg | 1 + frontend/public/light/train-simple-light.svg | 1 + frontend/public/light/tram-light.svg | 1 + frontend/public/light/translate-light.svg | 1 + frontend/public/light/trash-light.svg | 1 + frontend/public/light/trash-simple-light.svg | 1 + .../public/light/tray-arrow-down-light.svg | 1 + frontend/public/light/tray-arrow-up-light.svg | 1 + frontend/public/light/tray-light.svg | 1 + .../public/light/treasure-chest-light.svg | 1 + .../public/light/tree-evergreen-light.svg | 1 + frontend/public/light/tree-light.svg | 1 + frontend/public/light/tree-palm-light.svg | 1 + .../public/light/tree-structure-light.svg | 1 + frontend/public/light/tree-view-light.svg | 1 + frontend/public/light/trend-down-light.svg | 1 + frontend/public/light/trend-up-light.svg | 1 + .../public/light/triangle-dashed-light.svg | 1 + frontend/public/light/triangle-light.svg | 1 + frontend/public/light/trolley-light.svg | 1 + .../public/light/trolley-suitcase-light.svg | 1 + frontend/public/light/trophy-light.svg | 1 + frontend/public/light/truck-light.svg | 1 + frontend/public/light/truck-trailer-light.svg | 1 + frontend/public/light/tumblr-logo-light.svg | 1 + frontend/public/light/twitch-logo-light.svg | 1 + frontend/public/light/twitter-logo-light.svg | 1 + frontend/public/light/umbrella-light.svg | 1 + .../public/light/umbrella-simple-light.svg | 1 + frontend/public/light/union-light.svg | 1 + frontend/public/light/unite-light.svg | 1 + frontend/public/light/unite-square-light.svg | 1 + frontend/public/light/upload-light.svg | 1 + frontend/public/light/upload-simple-light.svg | 1 + frontend/public/light/usb-light.svg | 1 + frontend/public/light/user-check-light.svg | 1 + .../public/light/user-circle-check-light.svg | 1 + .../public/light/user-circle-dashed-light.svg | 1 + .../public/light/user-circle-gear-light.svg | 1 + frontend/public/light/user-circle-light.svg | 1 + .../public/light/user-circle-minus-light.svg | 1 + .../public/light/user-circle-plus-light.svg | 1 + frontend/public/light/user-focus-light.svg | 1 + frontend/public/light/user-gear-light.svg | 1 + frontend/public/light/user-light.svg | 1 + frontend/public/light/user-list-light.svg | 1 + frontend/public/light/user-minus-light.svg | 1 + frontend/public/light/user-plus-light.svg | 1 + .../public/light/user-rectangle-light.svg | 1 + frontend/public/light/user-sound-light.svg | 1 + frontend/public/light/user-square-light.svg | 1 + frontend/public/light/user-switch-light.svg | 1 + frontend/public/light/users-four-light.svg | 1 + frontend/public/light/users-light.svg | 1 + frontend/public/light/users-three-light.svg | 1 + frontend/public/light/van-light.svg | 1 + frontend/public/light/vault-light.svg | 1 + frontend/public/light/vector-three-light.svg | 1 + frontend/public/light/vector-two-light.svg | 1 + frontend/public/light/vibrate-light.svg | 1 + frontend/public/light/video-camera-light.svg | 1 + .../public/light/video-camera-slash-light.svg | 1 + .../public/light/video-conference-light.svg | 1 + frontend/public/light/video-light.svg | 1 + frontend/public/light/vignette-light.svg | 1 + frontend/public/light/vinyl-record-light.svg | 1 + .../public/light/virtual-reality-light.svg | 1 + frontend/public/light/virus-light.svg | 1 + frontend/public/light/visor-light.svg | 1 + frontend/public/light/voicemail-light.svg | 1 + frontend/public/light/volleyball-light.svg | 1 + frontend/public/light/wall-light.svg | 1 + frontend/public/light/wallet-light.svg | 1 + frontend/public/light/warehouse-light.svg | 1 + .../public/light/warning-circle-light.svg | 1 + .../public/light/warning-diamond-light.svg | 1 + frontend/public/light/warning-light.svg | 1 + .../public/light/warning-octagon-light.svg | 1 + .../public/light/washing-machine-light.svg | 1 + frontend/public/light/watch-light.svg | 1 + frontend/public/light/wave-sawtooth-light.svg | 1 + frontend/public/light/wave-sine-light.svg | 1 + frontend/public/light/wave-square-light.svg | 1 + frontend/public/light/wave-triangle-light.svg | 1 + frontend/public/light/waveform-light.svg | 1 + .../public/light/waveform-slash-light.svg | 1 + frontend/public/light/waves-light.svg | 1 + frontend/public/light/webcam-light.svg | 1 + frontend/public/light/webcam-slash-light.svg | 1 + frontend/public/light/webhooks-logo-light.svg | 1 + frontend/public/light/wechat-logo-light.svg | 1 + frontend/public/light/whatsapp-logo-light.svg | 1 + frontend/public/light/wheelchair-light.svg | 1 + .../public/light/wheelchair-motion-light.svg | 1 + frontend/public/light/wifi-high-light.svg | 1 + frontend/public/light/wifi-low-light.svg | 1 + frontend/public/light/wifi-medium-light.svg | 1 + frontend/public/light/wifi-none-light.svg | 1 + frontend/public/light/wifi-slash-light.svg | 1 + frontend/public/light/wifi-x-light.svg | 1 + frontend/public/light/wind-light.svg | 1 + frontend/public/light/windmill-light.svg | 1 + frontend/public/light/windows-logo-light.svg | 1 + frontend/public/light/wine-light.svg | 1 + frontend/public/light/wrench-light.svg | 1 + frontend/public/light/x-circle-light.svg | 1 + frontend/public/light/x-light.svg | 1 + frontend/public/light/x-logo-light.svg | 1 + frontend/public/light/x-square-light.svg | 1 + frontend/public/light/yarn-light.svg | 1 + frontend/public/light/yin-yang-light.svg | 1 + frontend/public/light/youtube-logo-light.svg | 1 + frontend/public/next.svg | 1 + frontend/public/vercel.svg | 1 + frontend/public/waras.webp | Bin 0 -> 25370 bytes .../waras_io/android-chrome-192x192.png | Bin 0 -> 19182 bytes .../waras_io/android-chrome-512x512.png | Bin 0 -> 102223 bytes frontend/public/waras_io/apple-touch-icon.png | Bin 0 -> 17617 bytes frontend/public/waras_io/favicon-16x16.png | Bin 0 -> 655 bytes frontend/public/waras_io/favicon-32x32.png | Bin 0 -> 1685 bytes frontend/public/waras_io/favicon.ico | Bin 0 -> 15406 bytes frontend/public/waras_io/site.webmanifest | 1 + frontend/public/window.svg | 1 + frontend/tsconfig.json | 34 + fuzzy-service/Dockerfile | 9 + fuzzy-service/fuzzy/__init__.py | 7 + fuzzy-service/fuzzy/engine.py | 207 + fuzzy-service/requirements.txt | 8 + fuzzy-service/service/__init__.py | 0 fuzzy-service/service/main.py | 100 + 1684 files changed, 33073 insertions(+) create mode 100644 .gitignore create mode 100644 backend/.env.example create mode 100644 backend/Dockerfile create mode 100644 backend/bun.lock create mode 100644 backend/package-lock.json create mode 100644 backend/package.json create mode 100644 backend/src/app.js create mode 100644 backend/src/db-init.js create mode 100644 backend/src/db.js create mode 100644 backend/src/routes/auth.routes.js create mode 100644 backend/src/routes/gas-station.routes.js create mode 100644 backend/src/routes/households.routes.js create mode 100644 backend/src/routes/land.routes.js create mode 100644 backend/src/routes/pertamina-fallback.json create mode 100644 backend/src/routes/poi.routes.js create mode 100644 backend/src/seed-data.js create mode 100644 backend/src/server.js create mode 100644 backend/src/users.js create mode 100644 docker-compose.coolify.yml create mode 100644 docker-compose.yml create mode 100644 frontend/.gitignore create mode 100644 frontend/AGENTS.md create mode 100644 frontend/CLAUDE.md create mode 100644 frontend/Dockerfile.frontend create mode 100644 frontend/README.md create mode 100644 frontend/app/auth/login/page.tsx create mode 100644 frontend/app/dashboard/gas-station/page.tsx create mode 100644 frontend/app/dashboard/land/page.tsx create mode 100644 frontend/app/dashboard/page.tsx create mode 100644 frontend/app/dashboard/poverty/page.tsx create mode 100644 frontend/app/dashboard/settings/page.tsx create mode 100644 frontend/app/favicon.ico create mode 100644 frontend/app/globals.css create mode 100644 frontend/app/layout.tsx create mode 100644 frontend/app/page.tsx create mode 100644 frontend/bun.lock create mode 100644 frontend/components.json create mode 100644 frontend/components/app-breadcrumbs.tsx create mode 100644 frontend/components/app-header.tsx create mode 100644 frontend/components/app-shared.tsx create mode 100644 frontend/components/app-shell.tsx create mode 100644 frontend/components/app-sidebar.tsx create mode 100644 frontend/components/auth-divider.tsx create mode 100644 frontend/components/auth-page.tsx create mode 100644 frontend/components/custom-sidebar-trigger.tsx create mode 100644 frontend/components/dashboard-activity.tsx create mode 100644 frontend/components/dashboard-card.tsx create mode 100644 frontend/components/dashboard-skeleton.tsx create mode 100644 frontend/components/dashboard.tsx create mode 100644 frontend/components/decor-icon.tsx create mode 100644 frontend/components/delta.tsx create mode 100644 frontend/components/floating-paths.tsx create mode 100644 frontend/components/formater.ts create mode 100644 frontend/components/gas-station/gas-station-directory.tsx create mode 100644 frontend/components/gas-station/gas-station-hud.tsx create mode 100644 frontend/components/gas-station/gas-station-inspector.tsx create mode 100644 frontend/components/gas-station/gas-station-stats-cards.tsx create mode 100644 frontend/components/gas-station/pertamina-price-table.tsx create mode 100644 frontend/components/gas-station/station-edit-dialog.tsx create mode 100644 frontend/components/gas-station/station-placement-dialog.tsx create mode 100644 frontend/components/header.tsx create mode 100644 frontend/components/households-trend-chart.tsx create mode 100644 frontend/components/icons/apple-icon.tsx create mode 100644 frontend/components/icons/github-icon.tsx create mode 100644 frontend/components/icons/google-icon.tsx create mode 100644 frontend/components/land/land-allocation-chart.tsx create mode 100644 frontend/components/land/land-directory.tsx create mode 100644 frontend/components/land/land-inspector.tsx create mode 100644 frontend/components/land/land-stats-cards.tsx create mode 100644 frontend/components/land/shape-edit-dialog.tsx create mode 100644 frontend/components/land/shape-placement-dialog.tsx create mode 100644 frontend/components/landing/landing-about.tsx create mode 100644 frontend/components/landing/landing-cta.tsx create mode 100644 frontend/components/landing/landing-footer.tsx create mode 100644 frontend/components/landing/landing-header.tsx create mode 100644 frontend/components/landing/landing-hero.tsx create mode 100644 frontend/components/landing/landing-projects.tsx create mode 100644 frontend/components/landing/link-icon.tsx create mode 100644 frontend/components/latest-change.tsx create mode 100644 frontend/components/leaflet-map.tsx create mode 100644 frontend/components/logo.tsx create mode 100644 frontend/components/maps/map-page-header.tsx create mode 100644 frontend/components/maps/maps-breadcrumb.tsx create mode 100644 frontend/components/mobile-nav.tsx create mode 100644 frontend/components/nav-group.tsx create mode 100644 frontend/components/nav-user.tsx create mode 100644 frontend/components/portal.tsx create mode 100644 frontend/components/poverty/fuzzy-priority-stats.tsx create mode 100644 frontend/components/poverty/fuzzy-result-step.tsx create mode 100644 frontend/components/poverty/marker-edit-dialog.tsx create mode 100644 frontend/components/poverty/marker-placement-dialog.tsx create mode 100644 frontend/components/poverty/poverty-directory.tsx create mode 100644 frontend/components/poverty/poverty-households-table.tsx create mode 100644 frontend/components/poverty/poverty-hud.tsx create mode 100644 frontend/components/poverty/poverty-inspector.tsx create mode 100644 frontend/components/poverty/poverty-stats-cards.tsx create mode 100644 frontend/components/poverty/poverty-trend-chart.tsx create mode 100644 frontend/components/poverty/radius-slider.tsx create mode 100644 frontend/components/poverty/religion-dialog.tsx create mode 100644 frontend/components/priority-breakdown.tsx create mode 100644 frontend/components/priority-score-chart.tsx create mode 100644 frontend/components/recent-households.tsx create mode 100644 frontend/components/shadcn-studio/breadcrumb/breadcrumb-03.tsx create mode 100644 frontend/components/stats.tsx create mode 100644 frontend/components/time-undo-action.tsx create mode 100644 frontend/components/ui/adaptive-slider.tsx create mode 100644 frontend/components/ui/avatar.tsx create mode 100644 frontend/components/ui/badge.tsx create mode 100644 frontend/components/ui/breadcrumb.tsx create mode 100644 frontend/components/ui/button.tsx create mode 100644 frontend/components/ui/card.tsx create mode 100644 frontend/components/ui/chart.tsx create mode 100644 frontend/components/ui/collapsible.tsx create mode 100644 frontend/components/ui/collection-grid-disclosure.tsx create mode 100644 frontend/components/ui/continuous-tabs.tsx create mode 100644 frontend/components/ui/cosmic-button.tsx create mode 100644 frontend/components/ui/dialog.tsx create mode 100644 frontend/components/ui/dropdown-menu.tsx create mode 100644 frontend/components/ui/dropdown.tsx create mode 100644 frontend/components/ui/empty.tsx create mode 100644 frontend/components/ui/input-group.tsx create mode 100644 frontend/components/ui/input.tsx create mode 100644 frontend/components/ui/kbd.tsx create mode 100644 frontend/components/ui/phosphor-icons.tsx create mode 100644 frontend/components/ui/separator.tsx create mode 100644 frontend/components/ui/sheet.tsx create mode 100644 frontend/components/ui/sidebar.tsx create mode 100644 frontend/components/ui/skeleton.tsx create mode 100644 frontend/components/ui/sonner.tsx create mode 100644 frontend/components/ui/table.tsx create mode 100644 frontend/components/ui/textarea.tsx create mode 100644 frontend/components/ui/tooltip.tsx create mode 100644 frontend/design/design.md create mode 100644 frontend/design/tailwind.css create mode 100644 frontend/design/token.json create mode 100644 frontend/design/variables.css create mode 100644 frontend/docs/superpowers/plans/2026-06-10-religion-dialog-radius-fix.md create mode 100644 frontend/docs/superpowers/specs/2026-06-10-religion-dialog-radius-fix-design.md create mode 100644 frontend/eslint.config.mjs create mode 100644 frontend/hooks/use-mobile.ts create mode 100644 frontend/hooks/use-scroll.ts create mode 100644 frontend/lib/auth-context.tsx create mode 100644 frontend/lib/auth-types.ts create mode 100644 frontend/lib/dashboard-types.ts create mode 100644 frontend/lib/map-data.ts create mode 100644 frontend/lib/poverty-types.ts create mode 100644 frontend/lib/utils.ts create mode 100644 frontend/next.config.ts create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/postcss.config.mjs create mode 100644 frontend/proxy.ts create mode 100644 frontend/public/file.svg create mode 100644 frontend/public/globe.svg create mode 100644 frontend/public/light/acorn-light.svg create mode 100644 frontend/public/light/address-book-light.svg create mode 100644 frontend/public/light/address-book-tabs-light.svg create mode 100644 frontend/public/light/air-traffic-control-light.svg create mode 100644 frontend/public/light/airplane-in-flight-light.svg create mode 100644 frontend/public/light/airplane-landing-light.svg create mode 100644 frontend/public/light/airplane-light.svg create mode 100644 frontend/public/light/airplane-takeoff-light.svg create mode 100644 frontend/public/light/airplane-taxiing-light.svg create mode 100644 frontend/public/light/airplane-tilt-light.svg create mode 100644 frontend/public/light/airplay-light.svg create mode 100644 frontend/public/light/alarm-light.svg create mode 100644 frontend/public/light/alien-light.svg create mode 100644 frontend/public/light/align-bottom-light.svg create mode 100644 frontend/public/light/align-bottom-simple-light.svg create mode 100644 frontend/public/light/align-center-horizontal-light.svg create mode 100644 frontend/public/light/align-center-horizontal-simple-light.svg create mode 100644 frontend/public/light/align-center-vertical-light.svg create mode 100644 frontend/public/light/align-center-vertical-simple-light.svg create mode 100644 frontend/public/light/align-left-light.svg create mode 100644 frontend/public/light/align-left-simple-light.svg create mode 100644 frontend/public/light/align-right-light.svg create mode 100644 frontend/public/light/align-right-simple-light.svg create mode 100644 frontend/public/light/align-top-light.svg create mode 100644 frontend/public/light/align-top-simple-light.svg create mode 100644 frontend/public/light/amazon-logo-light.svg create mode 100644 frontend/public/light/ambulance-light.svg create mode 100644 frontend/public/light/anchor-light.svg create mode 100644 frontend/public/light/anchor-simple-light.svg create mode 100644 frontend/public/light/android-logo-light.svg create mode 100644 frontend/public/light/angle-light.svg create mode 100644 frontend/public/light/angular-logo-light.svg create mode 100644 frontend/public/light/aperture-light.svg create mode 100644 frontend/public/light/app-store-logo-light.svg create mode 100644 frontend/public/light/app-window-light.svg create mode 100644 frontend/public/light/apple-logo-light.svg create mode 100644 frontend/public/light/apple-podcasts-logo-light.svg create mode 100644 frontend/public/light/approximate-equals-light.svg create mode 100644 frontend/public/light/archive-light.svg create mode 100644 frontend/public/light/armchair-light.svg create mode 100644 frontend/public/light/arrow-arc-left-light.svg create mode 100644 frontend/public/light/arrow-arc-right-light.svg create mode 100644 frontend/public/light/arrow-bend-double-up-left-light.svg create mode 100644 frontend/public/light/arrow-bend-double-up-right-light.svg create mode 100644 frontend/public/light/arrow-bend-down-left-light.svg create mode 100644 frontend/public/light/arrow-bend-down-right-light.svg create mode 100644 frontend/public/light/arrow-bend-left-down-light.svg create mode 100644 frontend/public/light/arrow-bend-left-up-light.svg create mode 100644 frontend/public/light/arrow-bend-right-down-light.svg create mode 100644 frontend/public/light/arrow-bend-right-up-light.svg create mode 100644 frontend/public/light/arrow-bend-up-left-light.svg create mode 100644 frontend/public/light/arrow-bend-up-right-light.svg create mode 100644 frontend/public/light/arrow-circle-down-left-light.svg create mode 100644 frontend/public/light/arrow-circle-down-light.svg create mode 100644 frontend/public/light/arrow-circle-down-right-light.svg create mode 100644 frontend/public/light/arrow-circle-left-light.svg create mode 100644 frontend/public/light/arrow-circle-right-light.svg create mode 100644 frontend/public/light/arrow-circle-up-left-light.svg create mode 100644 frontend/public/light/arrow-circle-up-light.svg create mode 100644 frontend/public/light/arrow-circle-up-right-light.svg create mode 100644 frontend/public/light/arrow-clockwise-light.svg create mode 100644 frontend/public/light/arrow-counter-clockwise-light.svg create mode 100644 frontend/public/light/arrow-down-left-light.svg create mode 100644 frontend/public/light/arrow-down-light.svg create mode 100644 frontend/public/light/arrow-down-right-light.svg create mode 100644 frontend/public/light/arrow-elbow-down-left-light.svg create mode 100644 frontend/public/light/arrow-elbow-down-right-light.svg create mode 100644 frontend/public/light/arrow-elbow-left-down-light.svg create mode 100644 frontend/public/light/arrow-elbow-left-light.svg create mode 100644 frontend/public/light/arrow-elbow-left-up-light.svg create mode 100644 frontend/public/light/arrow-elbow-right-down-light.svg create mode 100644 frontend/public/light/arrow-elbow-right-light.svg create mode 100644 frontend/public/light/arrow-elbow-right-up-light.svg create mode 100644 frontend/public/light/arrow-elbow-up-left-light.svg create mode 100644 frontend/public/light/arrow-elbow-up-right-light.svg create mode 100644 frontend/public/light/arrow-fat-down-light.svg create mode 100644 frontend/public/light/arrow-fat-left-light.svg create mode 100644 frontend/public/light/arrow-fat-line-down-light.svg create mode 100644 frontend/public/light/arrow-fat-line-left-light.svg create mode 100644 frontend/public/light/arrow-fat-line-right-light.svg create mode 100644 frontend/public/light/arrow-fat-line-up-light.svg create mode 100644 frontend/public/light/arrow-fat-lines-down-light.svg create mode 100644 frontend/public/light/arrow-fat-lines-left-light.svg create mode 100644 frontend/public/light/arrow-fat-lines-right-light.svg create mode 100644 frontend/public/light/arrow-fat-lines-up-light.svg create mode 100644 frontend/public/light/arrow-fat-right-light.svg create mode 100644 frontend/public/light/arrow-fat-up-light.svg create mode 100644 frontend/public/light/arrow-left-light.svg create mode 100644 frontend/public/light/arrow-line-down-left-light.svg create mode 100644 frontend/public/light/arrow-line-down-light.svg create mode 100644 frontend/public/light/arrow-line-down-right-light.svg create mode 100644 frontend/public/light/arrow-line-left-light.svg create mode 100644 frontend/public/light/arrow-line-right-light.svg create mode 100644 frontend/public/light/arrow-line-up-left-light.svg create mode 100644 frontend/public/light/arrow-line-up-light.svg create mode 100644 frontend/public/light/arrow-line-up-right-light.svg create mode 100644 frontend/public/light/arrow-right-light.svg create mode 100644 frontend/public/light/arrow-square-down-left-light.svg create mode 100644 frontend/public/light/arrow-square-down-light.svg create mode 100644 frontend/public/light/arrow-square-down-right-light.svg create mode 100644 frontend/public/light/arrow-square-in-light.svg create mode 100644 frontend/public/light/arrow-square-left-light.svg create mode 100644 frontend/public/light/arrow-square-out-light.svg create mode 100644 frontend/public/light/arrow-square-right-light.svg create mode 100644 frontend/public/light/arrow-square-up-left-light.svg create mode 100644 frontend/public/light/arrow-square-up-light.svg create mode 100644 frontend/public/light/arrow-square-up-right-light.svg create mode 100644 frontend/public/light/arrow-u-down-left-light.svg create mode 100644 frontend/public/light/arrow-u-down-right-light.svg create mode 100644 frontend/public/light/arrow-u-left-down-light.svg create mode 100644 frontend/public/light/arrow-u-left-up-light.svg create mode 100644 frontend/public/light/arrow-u-right-down-light.svg create mode 100644 frontend/public/light/arrow-u-right-up-light.svg create mode 100644 frontend/public/light/arrow-u-up-left-light.svg create mode 100644 frontend/public/light/arrow-u-up-right-light.svg create mode 100644 frontend/public/light/arrow-up-left-light.svg create mode 100644 frontend/public/light/arrow-up-light.svg create mode 100644 frontend/public/light/arrow-up-right-light.svg create mode 100644 frontend/public/light/arrows-clockwise-light.svg create mode 100644 frontend/public/light/arrows-counter-clockwise-light.svg create mode 100644 frontend/public/light/arrows-down-up-light.svg create mode 100644 frontend/public/light/arrows-horizontal-light.svg create mode 100644 frontend/public/light/arrows-in-cardinal-light.svg create mode 100644 frontend/public/light/arrows-in-light.svg create mode 100644 frontend/public/light/arrows-in-line-horizontal-light.svg create mode 100644 frontend/public/light/arrows-in-line-vertical-light.svg create mode 100644 frontend/public/light/arrows-in-simple-light.svg create mode 100644 frontend/public/light/arrows-left-right-light.svg create mode 100644 frontend/public/light/arrows-merge-light.svg create mode 100644 frontend/public/light/arrows-out-cardinal-light.svg create mode 100644 frontend/public/light/arrows-out-light.svg create mode 100644 frontend/public/light/arrows-out-line-horizontal-light.svg create mode 100644 frontend/public/light/arrows-out-line-vertical-light.svg create mode 100644 frontend/public/light/arrows-out-simple-light.svg create mode 100644 frontend/public/light/arrows-split-light.svg create mode 100644 frontend/public/light/arrows-vertical-light.svg create mode 100644 frontend/public/light/article-light.svg create mode 100644 frontend/public/light/article-medium-light.svg create mode 100644 frontend/public/light/article-ny-times-light.svg create mode 100644 frontend/public/light/asclepius-light.svg create mode 100644 frontend/public/light/asterisk-light.svg create mode 100644 frontend/public/light/asterisk-simple-light.svg create mode 100644 frontend/public/light/at-light.svg create mode 100644 frontend/public/light/atom-light.svg create mode 100644 frontend/public/light/avocado-light.svg create mode 100644 frontend/public/light/axe-light.svg create mode 100644 frontend/public/light/baby-carriage-light.svg create mode 100644 frontend/public/light/baby-light.svg create mode 100644 frontend/public/light/backpack-light.svg create mode 100644 frontend/public/light/backspace-light.svg create mode 100644 frontend/public/light/bag-light.svg create mode 100644 frontend/public/light/bag-simple-light.svg create mode 100644 frontend/public/light/balloon-light.svg create mode 100644 frontend/public/light/bandaids-light.svg create mode 100644 frontend/public/light/bank-light.svg create mode 100644 frontend/public/light/barbell-light.svg create mode 100644 frontend/public/light/barcode-light.svg create mode 100644 frontend/public/light/barn-light.svg create mode 100644 frontend/public/light/barricade-light.svg create mode 100644 frontend/public/light/baseball-cap-light.svg create mode 100644 frontend/public/light/baseball-helmet-light.svg create mode 100644 frontend/public/light/baseball-light.svg create mode 100644 frontend/public/light/basket-light.svg create mode 100644 frontend/public/light/basketball-light.svg create mode 100644 frontend/public/light/bathtub-light.svg create mode 100644 frontend/public/light/battery-charging-light.svg create mode 100644 frontend/public/light/battery-charging-vertical-light.svg create mode 100644 frontend/public/light/battery-empty-light.svg create mode 100644 frontend/public/light/battery-full-light.svg create mode 100644 frontend/public/light/battery-high-light.svg create mode 100644 frontend/public/light/battery-low-light.svg create mode 100644 frontend/public/light/battery-medium-light.svg create mode 100644 frontend/public/light/battery-plus-light.svg create mode 100644 frontend/public/light/battery-plus-vertical-light.svg create mode 100644 frontend/public/light/battery-vertical-empty-light.svg create mode 100644 frontend/public/light/battery-vertical-full-light.svg create mode 100644 frontend/public/light/battery-vertical-high-light.svg create mode 100644 frontend/public/light/battery-vertical-low-light.svg create mode 100644 frontend/public/light/battery-vertical-medium-light.svg create mode 100644 frontend/public/light/battery-warning-light.svg create mode 100644 frontend/public/light/battery-warning-vertical-light.svg create mode 100644 frontend/public/light/beach-ball-light.svg create mode 100644 frontend/public/light/beanie-light.svg create mode 100644 frontend/public/light/bed-light.svg create mode 100644 frontend/public/light/beer-bottle-light.svg create mode 100644 frontend/public/light/beer-stein-light.svg create mode 100644 frontend/public/light/behance-logo-light.svg create mode 100644 frontend/public/light/bell-light.svg create mode 100644 frontend/public/light/bell-ringing-light.svg create mode 100644 frontend/public/light/bell-simple-light.svg create mode 100644 frontend/public/light/bell-simple-ringing-light.svg create mode 100644 frontend/public/light/bell-simple-slash-light.svg create mode 100644 frontend/public/light/bell-simple-z-light.svg create mode 100644 frontend/public/light/bell-slash-light.svg create mode 100644 frontend/public/light/bell-z-light.svg create mode 100644 frontend/public/light/belt-light.svg create mode 100644 frontend/public/light/bezier-curve-light.svg create mode 100644 frontend/public/light/bicycle-light.svg create mode 100644 frontend/public/light/binary-light.svg create mode 100644 frontend/public/light/binoculars-light.svg create mode 100644 frontend/public/light/biohazard-light.svg create mode 100644 frontend/public/light/bird-light.svg create mode 100644 frontend/public/light/blueprint-light.svg create mode 100644 frontend/public/light/bluetooth-connected-light.svg create mode 100644 frontend/public/light/bluetooth-light.svg create mode 100644 frontend/public/light/bluetooth-slash-light.svg create mode 100644 frontend/public/light/bluetooth-x-light.svg create mode 100644 frontend/public/light/boat-light.svg create mode 100644 frontend/public/light/bomb-light.svg create mode 100644 frontend/public/light/bone-light.svg create mode 100644 frontend/public/light/book-bookmark-light.svg create mode 100644 frontend/public/light/book-light.svg create mode 100644 frontend/public/light/book-open-light.svg create mode 100644 frontend/public/light/book-open-text-light.svg create mode 100644 frontend/public/light/book-open-user-light.svg create mode 100644 frontend/public/light/bookmark-light.svg create mode 100644 frontend/public/light/bookmark-simple-light.svg create mode 100644 frontend/public/light/bookmarks-light.svg create mode 100644 frontend/public/light/bookmarks-simple-light.svg create mode 100644 frontend/public/light/books-light.svg create mode 100644 frontend/public/light/boot-light.svg create mode 100644 frontend/public/light/boules-light.svg create mode 100644 frontend/public/light/bounding-box-light.svg create mode 100644 frontend/public/light/bowl-food-light.svg create mode 100644 frontend/public/light/bowl-steam-light.svg create mode 100644 frontend/public/light/bowling-ball-light.svg create mode 100644 frontend/public/light/box-arrow-down-light.svg create mode 100644 frontend/public/light/box-arrow-up-light.svg create mode 100644 frontend/public/light/boxing-glove-light.svg create mode 100644 frontend/public/light/brackets-angle-light.svg create mode 100644 frontend/public/light/brackets-curly-light.svg create mode 100644 frontend/public/light/brackets-round-light.svg create mode 100644 frontend/public/light/brackets-square-light.svg create mode 100644 frontend/public/light/brain-light.svg create mode 100644 frontend/public/light/brandy-light.svg create mode 100644 frontend/public/light/bread-light.svg create mode 100644 frontend/public/light/bridge-light.svg create mode 100644 frontend/public/light/briefcase-light.svg create mode 100644 frontend/public/light/briefcase-metal-light.svg create mode 100644 frontend/public/light/broadcast-light.svg create mode 100644 frontend/public/light/broom-light.svg create mode 100644 frontend/public/light/browser-light.svg create mode 100644 frontend/public/light/browsers-light.svg create mode 100644 frontend/public/light/bug-beetle-light.svg create mode 100644 frontend/public/light/bug-droid-light.svg create mode 100644 frontend/public/light/bug-light.svg create mode 100644 frontend/public/light/building-apartment-light.svg create mode 100644 frontend/public/light/building-light.svg create mode 100644 frontend/public/light/building-office-light.svg create mode 100644 frontend/public/light/buildings-light.svg create mode 100644 frontend/public/light/bulldozer-light.svg create mode 100644 frontend/public/light/bus-light.svg create mode 100644 frontend/public/light/butterfly-light.svg create mode 100644 frontend/public/light/cable-car-light.svg create mode 100644 frontend/public/light/cactus-light.svg create mode 100644 frontend/public/light/cake-light.svg create mode 100644 frontend/public/light/calculator-light.svg create mode 100644 frontend/public/light/calendar-blank-light.svg create mode 100644 frontend/public/light/calendar-check-light.svg create mode 100644 frontend/public/light/calendar-dot-light.svg create mode 100644 frontend/public/light/calendar-dots-light.svg create mode 100644 frontend/public/light/calendar-heart-light.svg create mode 100644 frontend/public/light/calendar-light.svg create mode 100644 frontend/public/light/calendar-minus-light.svg create mode 100644 frontend/public/light/calendar-plus-light.svg create mode 100644 frontend/public/light/calendar-slash-light.svg create mode 100644 frontend/public/light/calendar-star-light.svg create mode 100644 frontend/public/light/calendar-x-light.svg create mode 100644 frontend/public/light/call-bell-light.svg create mode 100644 frontend/public/light/camera-light.svg create mode 100644 frontend/public/light/camera-plus-light.svg create mode 100644 frontend/public/light/camera-rotate-light.svg create mode 100644 frontend/public/light/camera-slash-light.svg create mode 100644 frontend/public/light/campfire-light.svg create mode 100644 frontend/public/light/car-battery-light.svg create mode 100644 frontend/public/light/car-light.svg create mode 100644 frontend/public/light/car-profile-light.svg create mode 100644 frontend/public/light/car-simple-light.svg create mode 100644 frontend/public/light/cardholder-light.svg create mode 100644 frontend/public/light/cards-light.svg create mode 100644 frontend/public/light/cards-three-light.svg create mode 100644 frontend/public/light/caret-circle-double-down-light.svg create mode 100644 frontend/public/light/caret-circle-double-left-light.svg create mode 100644 frontend/public/light/caret-circle-double-right-light.svg create mode 100644 frontend/public/light/caret-circle-double-up-light.svg create mode 100644 frontend/public/light/caret-circle-down-light.svg create mode 100644 frontend/public/light/caret-circle-left-light.svg create mode 100644 frontend/public/light/caret-circle-right-light.svg create mode 100644 frontend/public/light/caret-circle-up-down-light.svg create mode 100644 frontend/public/light/caret-circle-up-light.svg create mode 100644 frontend/public/light/caret-double-down-light.svg create mode 100644 frontend/public/light/caret-double-left-light.svg create mode 100644 frontend/public/light/caret-double-right-light.svg create mode 100644 frontend/public/light/caret-double-up-light.svg create mode 100644 frontend/public/light/caret-down-light.svg create mode 100644 frontend/public/light/caret-left-light.svg create mode 100644 frontend/public/light/caret-line-down-light.svg create mode 100644 frontend/public/light/caret-line-left-light.svg create mode 100644 frontend/public/light/caret-line-right-light.svg create mode 100644 frontend/public/light/caret-line-up-light.svg create mode 100644 frontend/public/light/caret-right-light.svg create mode 100644 frontend/public/light/caret-up-down-light.svg create mode 100644 frontend/public/light/caret-up-light.svg create mode 100644 frontend/public/light/carrot-light.svg create mode 100644 frontend/public/light/cash-register-light.svg create mode 100644 frontend/public/light/cassette-tape-light.svg create mode 100644 frontend/public/light/castle-turret-light.svg create mode 100644 frontend/public/light/cat-light.svg create mode 100644 frontend/public/light/cell-signal-full-light.svg create mode 100644 frontend/public/light/cell-signal-high-light.svg create mode 100644 frontend/public/light/cell-signal-low-light.svg create mode 100644 frontend/public/light/cell-signal-medium-light.svg create mode 100644 frontend/public/light/cell-signal-none-light.svg create mode 100644 frontend/public/light/cell-signal-slash-light.svg create mode 100644 frontend/public/light/cell-signal-x-light.svg create mode 100644 frontend/public/light/cell-tower-light.svg create mode 100644 frontend/public/light/certificate-light.svg create mode 100644 frontend/public/light/chair-light.svg create mode 100644 frontend/public/light/chalkboard-light.svg create mode 100644 frontend/public/light/chalkboard-simple-light.svg create mode 100644 frontend/public/light/chalkboard-teacher-light.svg create mode 100644 frontend/public/light/champagne-light.svg create mode 100644 frontend/public/light/charging-station-light.svg create mode 100644 frontend/public/light/chart-bar-horizontal-light.svg create mode 100644 frontend/public/light/chart-bar-light.svg create mode 100644 frontend/public/light/chart-donut-light.svg create mode 100644 frontend/public/light/chart-line-down-light.svg create mode 100644 frontend/public/light/chart-line-light.svg create mode 100644 frontend/public/light/chart-line-up-light.svg create mode 100644 frontend/public/light/chart-pie-light.svg create mode 100644 frontend/public/light/chart-pie-slice-light.svg create mode 100644 frontend/public/light/chart-polar-light.svg create mode 100644 frontend/public/light/chart-scatter-light.svg create mode 100644 frontend/public/light/chat-centered-dots-light.svg create mode 100644 frontend/public/light/chat-centered-light.svg create mode 100644 frontend/public/light/chat-centered-slash-light.svg create mode 100644 frontend/public/light/chat-centered-text-light.svg create mode 100644 frontend/public/light/chat-circle-dots-light.svg create mode 100644 frontend/public/light/chat-circle-light.svg create mode 100644 frontend/public/light/chat-circle-slash-light.svg create mode 100644 frontend/public/light/chat-circle-text-light.svg create mode 100644 frontend/public/light/chat-dots-light.svg create mode 100644 frontend/public/light/chat-light.svg create mode 100644 frontend/public/light/chat-slash-light.svg create mode 100644 frontend/public/light/chat-teardrop-dots-light.svg create mode 100644 frontend/public/light/chat-teardrop-light.svg create mode 100644 frontend/public/light/chat-teardrop-slash-light.svg create mode 100644 frontend/public/light/chat-teardrop-text-light.svg create mode 100644 frontend/public/light/chat-text-light.svg create mode 100644 frontend/public/light/chats-circle-light.svg create mode 100644 frontend/public/light/chats-light.svg create mode 100644 frontend/public/light/chats-teardrop-light.svg create mode 100644 frontend/public/light/check-circle-light.svg create mode 100644 frontend/public/light/check-fat-light.svg create mode 100644 frontend/public/light/check-light.svg create mode 100644 frontend/public/light/check-square-light.svg create mode 100644 frontend/public/light/check-square-offset-light.svg create mode 100644 frontend/public/light/checkerboard-light.svg create mode 100644 frontend/public/light/checks-light.svg create mode 100644 frontend/public/light/cheers-light.svg create mode 100644 frontend/public/light/cheese-light.svg create mode 100644 frontend/public/light/chef-hat-light.svg create mode 100644 frontend/public/light/cherries-light.svg create mode 100644 frontend/public/light/church-light.svg create mode 100644 frontend/public/light/cigarette-light.svg create mode 100644 frontend/public/light/cigarette-slash-light.svg create mode 100644 frontend/public/light/circle-dashed-light.svg create mode 100644 frontend/public/light/circle-half-light.svg create mode 100644 frontend/public/light/circle-half-tilt-light.svg create mode 100644 frontend/public/light/circle-light.svg create mode 100644 frontend/public/light/circle-notch-light.svg create mode 100644 frontend/public/light/circles-four-light.svg create mode 100644 frontend/public/light/circles-three-light.svg create mode 100644 frontend/public/light/circles-three-plus-light.svg create mode 100644 frontend/public/light/circuitry-light.svg create mode 100644 frontend/public/light/city-light.svg create mode 100644 frontend/public/light/clipboard-light.svg create mode 100644 frontend/public/light/clipboard-text-light.svg create mode 100644 frontend/public/light/clock-afternoon-light.svg create mode 100644 frontend/public/light/clock-clockwise-light.svg create mode 100644 frontend/public/light/clock-countdown-light.svg create mode 100644 frontend/public/light/clock-counter-clockwise-light.svg create mode 100644 frontend/public/light/clock-light.svg create mode 100644 frontend/public/light/clock-user-light.svg create mode 100644 frontend/public/light/closed-captioning-light.svg create mode 100644 frontend/public/light/cloud-arrow-down-light.svg create mode 100644 frontend/public/light/cloud-arrow-up-light.svg create mode 100644 frontend/public/light/cloud-check-light.svg create mode 100644 frontend/public/light/cloud-fog-light.svg create mode 100644 frontend/public/light/cloud-light.svg create mode 100644 frontend/public/light/cloud-lightning-light.svg create mode 100644 frontend/public/light/cloud-moon-light.svg create mode 100644 frontend/public/light/cloud-rain-light.svg create mode 100644 frontend/public/light/cloud-slash-light.svg create mode 100644 frontend/public/light/cloud-snow-light.svg create mode 100644 frontend/public/light/cloud-sun-light.svg create mode 100644 frontend/public/light/cloud-warning-light.svg create mode 100644 frontend/public/light/cloud-x-light.svg create mode 100644 frontend/public/light/clover-light.svg create mode 100644 frontend/public/light/club-light.svg create mode 100644 frontend/public/light/coat-hanger-light.svg create mode 100644 frontend/public/light/coda-logo-light.svg create mode 100644 frontend/public/light/code-block-light.svg create mode 100644 frontend/public/light/code-light.svg create mode 100644 frontend/public/light/code-simple-light.svg create mode 100644 frontend/public/light/codepen-logo-light.svg create mode 100644 frontend/public/light/codesandbox-logo-light.svg create mode 100644 frontend/public/light/coffee-bean-light.svg create mode 100644 frontend/public/light/coffee-light.svg create mode 100644 frontend/public/light/coin-light.svg create mode 100644 frontend/public/light/coin-vertical-light.svg create mode 100644 frontend/public/light/coins-light.svg create mode 100644 frontend/public/light/columns-light.svg create mode 100644 frontend/public/light/columns-plus-left-light.svg create mode 100644 frontend/public/light/columns-plus-right-light.svg create mode 100644 frontend/public/light/command-light.svg create mode 100644 frontend/public/light/compass-light.svg create mode 100644 frontend/public/light/compass-rose-light.svg create mode 100644 frontend/public/light/compass-tool-light.svg create mode 100644 frontend/public/light/computer-tower-light.svg create mode 100644 frontend/public/light/confetti-light.svg create mode 100644 frontend/public/light/contactless-payment-light.svg create mode 100644 frontend/public/light/control-light.svg create mode 100644 frontend/public/light/cookie-light.svg create mode 100644 frontend/public/light/cooking-pot-light.svg create mode 100644 frontend/public/light/copy-light.svg create mode 100644 frontend/public/light/copy-simple-light.svg create mode 100644 frontend/public/light/copyleft-light.svg create mode 100644 frontend/public/light/copyright-light.svg create mode 100644 frontend/public/light/corners-in-light.svg create mode 100644 frontend/public/light/corners-out-light.svg create mode 100644 frontend/public/light/couch-light.svg create mode 100644 frontend/public/light/court-basketball-light.svg create mode 100644 frontend/public/light/cow-light.svg create mode 100644 frontend/public/light/cowboy-hat-light.svg create mode 100644 frontend/public/light/cpu-light.svg create mode 100644 frontend/public/light/crane-light.svg create mode 100644 frontend/public/light/crane-tower-light.svg create mode 100644 frontend/public/light/credit-card-light.svg create mode 100644 frontend/public/light/cricket-light.svg create mode 100644 frontend/public/light/crop-light.svg create mode 100644 frontend/public/light/cross-light.svg create mode 100644 frontend/public/light/crosshair-light.svg create mode 100644 frontend/public/light/crosshair-simple-light.svg create mode 100644 frontend/public/light/crown-cross-light.svg create mode 100644 frontend/public/light/crown-light.svg create mode 100644 frontend/public/light/crown-simple-light.svg create mode 100644 frontend/public/light/cube-focus-light.svg create mode 100644 frontend/public/light/cube-light.svg create mode 100644 frontend/public/light/cube-transparent-light.svg create mode 100644 frontend/public/light/currency-btc-light.svg create mode 100644 frontend/public/light/currency-circle-dollar-light.svg create mode 100644 frontend/public/light/currency-cny-light.svg create mode 100644 frontend/public/light/currency-dollar-light.svg create mode 100644 frontend/public/light/currency-dollar-simple-light.svg create mode 100644 frontend/public/light/currency-eth-light.svg create mode 100644 frontend/public/light/currency-eur-light.svg create mode 100644 frontend/public/light/currency-gbp-light.svg create mode 100644 frontend/public/light/currency-inr-light.svg create mode 100644 frontend/public/light/currency-jpy-light.svg create mode 100644 frontend/public/light/currency-krw-light.svg create mode 100644 frontend/public/light/currency-kzt-light.svg create mode 100644 frontend/public/light/currency-ngn-light.svg create mode 100644 frontend/public/light/currency-rub-light.svg create mode 100644 frontend/public/light/cursor-click-light.svg create mode 100644 frontend/public/light/cursor-light.svg create mode 100644 frontend/public/light/cursor-text-light.svg create mode 100644 frontend/public/light/cylinder-light.svg create mode 100644 frontend/public/light/database-light.svg create mode 100644 frontend/public/light/desk-light.svg create mode 100644 frontend/public/light/desktop-light.svg create mode 100644 frontend/public/light/desktop-tower-light.svg create mode 100644 frontend/public/light/detective-light.svg create mode 100644 frontend/public/light/dev-to-logo-light.svg create mode 100644 frontend/public/light/device-mobile-camera-light.svg create mode 100644 frontend/public/light/device-mobile-light.svg create mode 100644 frontend/public/light/device-mobile-slash-light.svg create mode 100644 frontend/public/light/device-mobile-speaker-light.svg create mode 100644 frontend/public/light/device-rotate-light.svg create mode 100644 frontend/public/light/device-tablet-camera-light.svg create mode 100644 frontend/public/light/device-tablet-light.svg create mode 100644 frontend/public/light/device-tablet-speaker-light.svg create mode 100644 frontend/public/light/devices-light.svg create mode 100644 frontend/public/light/diamond-light.svg create mode 100644 frontend/public/light/diamonds-four-light.svg create mode 100644 frontend/public/light/dice-five-light.svg create mode 100644 frontend/public/light/dice-four-light.svg create mode 100644 frontend/public/light/dice-one-light.svg create mode 100644 frontend/public/light/dice-six-light.svg create mode 100644 frontend/public/light/dice-three-light.svg create mode 100644 frontend/public/light/dice-two-light.svg create mode 100644 frontend/public/light/disc-light.svg create mode 100644 frontend/public/light/disco-ball-light.svg create mode 100644 frontend/public/light/discord-logo-light.svg create mode 100644 frontend/public/light/divide-light.svg create mode 100644 frontend/public/light/dna-light.svg create mode 100644 frontend/public/light/dog-light.svg create mode 100644 frontend/public/light/door-light.svg create mode 100644 frontend/public/light/door-open-light.svg create mode 100644 frontend/public/light/dot-light.svg create mode 100644 frontend/public/light/dot-outline-light.svg create mode 100644 frontend/public/light/dots-nine-light.svg create mode 100644 frontend/public/light/dots-six-light.svg create mode 100644 frontend/public/light/dots-six-vertical-light.svg create mode 100644 frontend/public/light/dots-three-circle-light.svg create mode 100644 frontend/public/light/dots-three-circle-vertical-light.svg create mode 100644 frontend/public/light/dots-three-light.svg create mode 100644 frontend/public/light/dots-three-outline-light.svg create mode 100644 frontend/public/light/dots-three-outline-vertical-light.svg create mode 100644 frontend/public/light/dots-three-vertical-light.svg create mode 100644 frontend/public/light/download-light.svg create mode 100644 frontend/public/light/download-simple-light.svg create mode 100644 frontend/public/light/dress-light.svg create mode 100644 frontend/public/light/dresser-light.svg create mode 100644 frontend/public/light/dribbble-logo-light.svg create mode 100644 frontend/public/light/drone-light.svg create mode 100644 frontend/public/light/drop-half-bottom-light.svg create mode 100644 frontend/public/light/drop-half-light.svg create mode 100644 frontend/public/light/drop-light.svg create mode 100644 frontend/public/light/drop-simple-light.svg create mode 100644 frontend/public/light/drop-slash-light.svg create mode 100644 frontend/public/light/dropbox-logo-light.svg create mode 100644 frontend/public/light/ear-light.svg create mode 100644 frontend/public/light/ear-slash-light.svg create mode 100644 frontend/public/light/egg-crack-light.svg create mode 100644 frontend/public/light/egg-light.svg create mode 100644 frontend/public/light/eject-light.svg create mode 100644 frontend/public/light/eject-simple-light.svg create mode 100644 frontend/public/light/elevator-light.svg create mode 100644 frontend/public/light/empty-light.svg create mode 100644 frontend/public/light/engine-light.svg create mode 100644 frontend/public/light/envelope-light.svg create mode 100644 frontend/public/light/envelope-open-light.svg create mode 100644 frontend/public/light/envelope-simple-light.svg create mode 100644 frontend/public/light/envelope-simple-open-light.svg create mode 100644 frontend/public/light/equalizer-light.svg create mode 100644 frontend/public/light/equals-light.svg create mode 100644 frontend/public/light/eraser-light.svg create mode 100644 frontend/public/light/escalator-down-light.svg create mode 100644 frontend/public/light/escalator-up-light.svg create mode 100644 frontend/public/light/exam-light.svg create mode 100644 frontend/public/light/exclamation-mark-light.svg create mode 100644 frontend/public/light/exclude-light.svg create mode 100644 frontend/public/light/exclude-square-light.svg create mode 100644 frontend/public/light/export-light.svg create mode 100644 frontend/public/light/eye-closed-light.svg create mode 100644 frontend/public/light/eye-light.svg create mode 100644 frontend/public/light/eye-slash-light.svg create mode 100644 frontend/public/light/eyedropper-light.svg create mode 100644 frontend/public/light/eyedropper-sample-light.svg create mode 100644 frontend/public/light/eyeglasses-light.svg create mode 100644 frontend/public/light/eyes-light.svg create mode 100644 frontend/public/light/face-mask-light.svg create mode 100644 frontend/public/light/facebook-logo-light.svg create mode 100644 frontend/public/light/factory-light.svg create mode 100644 frontend/public/light/faders-horizontal-light.svg create mode 100644 frontend/public/light/faders-light.svg create mode 100644 frontend/public/light/fallout-shelter-light.svg create mode 100644 frontend/public/light/fan-light.svg create mode 100644 frontend/public/light/farm-light.svg create mode 100644 frontend/public/light/fast-forward-circle-light.svg create mode 100644 frontend/public/light/fast-forward-light.svg create mode 100644 frontend/public/light/feather-light.svg create mode 100644 frontend/public/light/fediverse-logo-light.svg create mode 100644 frontend/public/light/figma-logo-light.svg create mode 100644 frontend/public/light/file-archive-light.svg create mode 100644 frontend/public/light/file-arrow-down-light.svg create mode 100644 frontend/public/light/file-arrow-up-light.svg create mode 100644 frontend/public/light/file-audio-light.svg create mode 100644 frontend/public/light/file-c-light.svg create mode 100644 frontend/public/light/file-c-sharp-light.svg create mode 100644 frontend/public/light/file-cloud-light.svg create mode 100644 frontend/public/light/file-code-light.svg create mode 100644 frontend/public/light/file-cpp-light.svg create mode 100644 frontend/public/light/file-css-light.svg create mode 100644 frontend/public/light/file-csv-light.svg create mode 100644 frontend/public/light/file-dashed-light.svg create mode 100644 frontend/public/light/file-doc-light.svg create mode 100644 frontend/public/light/file-html-light.svg create mode 100644 frontend/public/light/file-image-light.svg create mode 100644 frontend/public/light/file-ini-light.svg create mode 100644 frontend/public/light/file-jpg-light.svg create mode 100644 frontend/public/light/file-js-light.svg create mode 100644 frontend/public/light/file-jsx-light.svg create mode 100644 frontend/public/light/file-light.svg create mode 100644 frontend/public/light/file-lock-light.svg create mode 100644 frontend/public/light/file-magnifying-glass-light.svg create mode 100644 frontend/public/light/file-md-light.svg create mode 100644 frontend/public/light/file-minus-light.svg create mode 100644 frontend/public/light/file-pdf-light.svg create mode 100644 frontend/public/light/file-plus-light.svg create mode 100644 frontend/public/light/file-png-light.svg create mode 100644 frontend/public/light/file-ppt-light.svg create mode 100644 frontend/public/light/file-py-light.svg create mode 100644 frontend/public/light/file-rs-light.svg create mode 100644 frontend/public/light/file-sql-light.svg create mode 100644 frontend/public/light/file-svg-light.svg create mode 100644 frontend/public/light/file-text-light.svg create mode 100644 frontend/public/light/file-ts-light.svg create mode 100644 frontend/public/light/file-tsx-light.svg create mode 100644 frontend/public/light/file-txt-light.svg create mode 100644 frontend/public/light/file-video-light.svg create mode 100644 frontend/public/light/file-vue-light.svg create mode 100644 frontend/public/light/file-x-light.svg create mode 100644 frontend/public/light/file-xls-light.svg create mode 100644 frontend/public/light/file-zip-light.svg create mode 100644 frontend/public/light/files-light.svg create mode 100644 frontend/public/light/film-reel-light.svg create mode 100644 frontend/public/light/film-script-light.svg create mode 100644 frontend/public/light/film-slate-light.svg create mode 100644 frontend/public/light/film-strip-light.svg create mode 100644 frontend/public/light/fingerprint-light.svg create mode 100644 frontend/public/light/fingerprint-simple-light.svg create mode 100644 frontend/public/light/finn-the-human-light.svg create mode 100644 frontend/public/light/fire-extinguisher-light.svg create mode 100644 frontend/public/light/fire-light.svg create mode 100644 frontend/public/light/fire-simple-light.svg create mode 100644 frontend/public/light/fire-truck-light.svg create mode 100644 frontend/public/light/first-aid-kit-light.svg create mode 100644 frontend/public/light/first-aid-light.svg create mode 100644 frontend/public/light/fish-light.svg create mode 100644 frontend/public/light/fish-simple-light.svg create mode 100644 frontend/public/light/flag-banner-fold-light.svg create mode 100644 frontend/public/light/flag-banner-light.svg create mode 100644 frontend/public/light/flag-checkered-light.svg create mode 100644 frontend/public/light/flag-light.svg create mode 100644 frontend/public/light/flag-pennant-light.svg create mode 100644 frontend/public/light/flame-light.svg create mode 100644 frontend/public/light/flashlight-light.svg create mode 100644 frontend/public/light/flask-light.svg create mode 100644 frontend/public/light/flip-horizontal-light.svg create mode 100644 frontend/public/light/flip-vertical-light.svg create mode 100644 frontend/public/light/floppy-disk-back-light.svg create mode 100644 frontend/public/light/floppy-disk-light.svg create mode 100644 frontend/public/light/flow-arrow-light.svg create mode 100644 frontend/public/light/flower-light.svg create mode 100644 frontend/public/light/flower-lotus-light.svg create mode 100644 frontend/public/light/flower-tulip-light.svg create mode 100644 frontend/public/light/flying-saucer-light.svg create mode 100644 frontend/public/light/folder-dashed-light.svg create mode 100644 frontend/public/light/folder-light.svg create mode 100644 frontend/public/light/folder-lock-light.svg create mode 100644 frontend/public/light/folder-minus-light.svg create mode 100644 frontend/public/light/folder-open-light.svg create mode 100644 frontend/public/light/folder-plus-light.svg create mode 100644 frontend/public/light/folder-simple-dashed-light.svg create mode 100644 frontend/public/light/folder-simple-light.svg create mode 100644 frontend/public/light/folder-simple-lock-light.svg create mode 100644 frontend/public/light/folder-simple-minus-light.svg create mode 100644 frontend/public/light/folder-simple-plus-light.svg create mode 100644 frontend/public/light/folder-simple-star-light.svg create mode 100644 frontend/public/light/folder-simple-user-light.svg create mode 100644 frontend/public/light/folder-star-light.svg create mode 100644 frontend/public/light/folder-user-light.svg create mode 100644 frontend/public/light/folders-light.svg create mode 100644 frontend/public/light/football-helmet-light.svg create mode 100644 frontend/public/light/football-light.svg create mode 100644 frontend/public/light/footprints-light.svg create mode 100644 frontend/public/light/fork-knife-light.svg create mode 100644 frontend/public/light/four-k-light.svg create mode 100644 frontend/public/light/frame-corners-light.svg create mode 100644 frontend/public/light/framer-logo-light.svg create mode 100644 frontend/public/light/function-light.svg create mode 100644 frontend/public/light/funnel-light.svg create mode 100644 frontend/public/light/funnel-simple-light.svg create mode 100644 frontend/public/light/funnel-simple-x-light.svg create mode 100644 frontend/public/light/funnel-x-light.svg create mode 100644 frontend/public/light/game-controller-light.svg create mode 100644 frontend/public/light/garage-light.svg create mode 100644 frontend/public/light/gas-can-light.svg create mode 100644 frontend/public/light/gas-pump-light.svg create mode 100644 frontend/public/light/gauge-light.svg create mode 100644 frontend/public/light/gavel-light.svg create mode 100644 frontend/public/light/gear-fine-light.svg create mode 100644 frontend/public/light/gear-light.svg create mode 100644 frontend/public/light/gear-six-light.svg create mode 100644 frontend/public/light/gender-female-light.svg create mode 100644 frontend/public/light/gender-intersex-light.svg create mode 100644 frontend/public/light/gender-male-light.svg create mode 100644 frontend/public/light/gender-neuter-light.svg create mode 100644 frontend/public/light/gender-nonbinary-light.svg create mode 100644 frontend/public/light/gender-transgender-light.svg create mode 100644 frontend/public/light/ghost-light.svg create mode 100644 frontend/public/light/gif-light.svg create mode 100644 frontend/public/light/gift-light.svg create mode 100644 frontend/public/light/git-branch-light.svg create mode 100644 frontend/public/light/git-commit-light.svg create mode 100644 frontend/public/light/git-diff-light.svg create mode 100644 frontend/public/light/git-fork-light.svg create mode 100644 frontend/public/light/git-merge-light.svg create mode 100644 frontend/public/light/git-pull-request-light.svg create mode 100644 frontend/public/light/github-logo-light.svg create mode 100644 frontend/public/light/gitlab-logo-light.svg create mode 100644 frontend/public/light/gitlab-logo-simple-light.svg create mode 100644 frontend/public/light/globe-hemisphere-east-light.svg create mode 100644 frontend/public/light/globe-hemisphere-west-light.svg create mode 100644 frontend/public/light/globe-light.svg create mode 100644 frontend/public/light/globe-simple-light.svg create mode 100644 frontend/public/light/globe-simple-x-light.svg create mode 100644 frontend/public/light/globe-stand-light.svg create mode 100644 frontend/public/light/globe-x-light.svg create mode 100644 frontend/public/light/goggles-light.svg create mode 100644 frontend/public/light/golf-light.svg create mode 100644 frontend/public/light/goodreads-logo-light.svg create mode 100644 frontend/public/light/google-cardboard-logo-light.svg create mode 100644 frontend/public/light/google-chrome-logo-light.svg create mode 100644 frontend/public/light/google-drive-logo-light.svg create mode 100644 frontend/public/light/google-logo-light.svg create mode 100644 frontend/public/light/google-photos-logo-light.svg create mode 100644 frontend/public/light/google-play-logo-light.svg create mode 100644 frontend/public/light/google-podcasts-logo-light.svg create mode 100644 frontend/public/light/gps-fix-light.svg create mode 100644 frontend/public/light/gps-light.svg create mode 100644 frontend/public/light/gps-slash-light.svg create mode 100644 frontend/public/light/gradient-light.svg create mode 100644 frontend/public/light/graduation-cap-light.svg create mode 100644 frontend/public/light/grains-light.svg create mode 100644 frontend/public/light/grains-slash-light.svg create mode 100644 frontend/public/light/graph-light.svg create mode 100644 frontend/public/light/graphics-card-light.svg create mode 100644 frontend/public/light/greater-than-light.svg create mode 100644 frontend/public/light/greater-than-or-equal-light.svg create mode 100644 frontend/public/light/grid-four-light.svg create mode 100644 frontend/public/light/grid-nine-light.svg create mode 100644 frontend/public/light/guitar-light.svg create mode 100644 frontend/public/light/hair-dryer-light.svg create mode 100644 frontend/public/light/hamburger-light.svg create mode 100644 frontend/public/light/hammer-light.svg create mode 100644 frontend/public/light/hand-arrow-down-light.svg create mode 100644 frontend/public/light/hand-arrow-up-light.svg create mode 100644 frontend/public/light/hand-coins-light.svg create mode 100644 frontend/public/light/hand-deposit-light.svg create mode 100644 frontend/public/light/hand-eye-light.svg create mode 100644 frontend/public/light/hand-fist-light.svg create mode 100644 frontend/public/light/hand-grabbing-light.svg create mode 100644 frontend/public/light/hand-heart-light.svg create mode 100644 frontend/public/light/hand-light.svg create mode 100644 frontend/public/light/hand-palm-light.svg create mode 100644 frontend/public/light/hand-peace-light.svg create mode 100644 frontend/public/light/hand-pointing-light.svg create mode 100644 frontend/public/light/hand-soap-light.svg create mode 100644 frontend/public/light/hand-swipe-left-light.svg create mode 100644 frontend/public/light/hand-swipe-right-light.svg create mode 100644 frontend/public/light/hand-tap-light.svg create mode 100644 frontend/public/light/hand-waving-light.svg create mode 100644 frontend/public/light/hand-withdraw-light.svg create mode 100644 frontend/public/light/handbag-light.svg create mode 100644 frontend/public/light/handbag-simple-light.svg create mode 100644 frontend/public/light/hands-clapping-light.svg create mode 100644 frontend/public/light/hands-praying-light.svg create mode 100644 frontend/public/light/handshake-light.svg create mode 100644 frontend/public/light/hard-drive-light.svg create mode 100644 frontend/public/light/hard-drives-light.svg create mode 100644 frontend/public/light/hard-hat-light.svg create mode 100644 frontend/public/light/hash-light.svg create mode 100644 frontend/public/light/hash-straight-light.svg create mode 100644 frontend/public/light/head-circuit-light.svg create mode 100644 frontend/public/light/headlights-light.svg create mode 100644 frontend/public/light/headphones-light.svg create mode 100644 frontend/public/light/headset-light.svg create mode 100644 frontend/public/light/heart-break-light.svg create mode 100644 frontend/public/light/heart-half-light.svg create mode 100644 frontend/public/light/heart-light.svg create mode 100644 frontend/public/light/heart-straight-break-light.svg create mode 100644 frontend/public/light/heart-straight-light.svg create mode 100644 frontend/public/light/heartbeat-light.svg create mode 100644 frontend/public/light/hexagon-light.svg create mode 100644 frontend/public/light/high-definition-light.svg create mode 100644 frontend/public/light/high-heel-light.svg create mode 100644 frontend/public/light/highlighter-circle-light.svg create mode 100644 frontend/public/light/highlighter-light.svg create mode 100644 frontend/public/light/hockey-light.svg create mode 100644 frontend/public/light/hoodie-light.svg create mode 100644 frontend/public/light/horse-light.svg create mode 100644 frontend/public/light/hospital-light.svg create mode 100644 frontend/public/light/hourglass-high-light.svg create mode 100644 frontend/public/light/hourglass-light.svg create mode 100644 frontend/public/light/hourglass-low-light.svg create mode 100644 frontend/public/light/hourglass-medium-light.svg create mode 100644 frontend/public/light/hourglass-simple-high-light.svg create mode 100644 frontend/public/light/hourglass-simple-light.svg create mode 100644 frontend/public/light/hourglass-simple-low-light.svg create mode 100644 frontend/public/light/hourglass-simple-medium-light.svg create mode 100644 frontend/public/light/house-light.svg create mode 100644 frontend/public/light/house-line-light.svg create mode 100644 frontend/public/light/house-simple-light.svg create mode 100644 frontend/public/light/hurricane-light.svg create mode 100644 frontend/public/light/ice-cream-light.svg create mode 100644 frontend/public/light/identification-badge-light.svg create mode 100644 frontend/public/light/identification-card-light.svg create mode 100644 frontend/public/light/image-broken-light.svg create mode 100644 frontend/public/light/image-light.svg create mode 100644 frontend/public/light/image-square-light.svg create mode 100644 frontend/public/light/images-light.svg create mode 100644 frontend/public/light/images-square-light.svg create mode 100644 frontend/public/light/infinity-light.svg create mode 100644 frontend/public/light/info-light.svg create mode 100644 frontend/public/light/instagram-logo-light.svg create mode 100644 frontend/public/light/intersect-light.svg create mode 100644 frontend/public/light/intersect-square-light.svg create mode 100644 frontend/public/light/intersect-three-light.svg create mode 100644 frontend/public/light/intersection-light.svg create mode 100644 frontend/public/light/invoice-light.svg create mode 100644 frontend/public/light/island-light.svg create mode 100644 frontend/public/light/jar-label-light.svg create mode 100644 frontend/public/light/jar-light.svg create mode 100644 frontend/public/light/jeep-light.svg create mode 100644 frontend/public/light/joystick-light.svg create mode 100644 frontend/public/light/kanban-light.svg create mode 100644 frontend/public/light/key-light.svg create mode 100644 frontend/public/light/key-return-light.svg create mode 100644 frontend/public/light/keyboard-light.svg create mode 100644 frontend/public/light/keyhole-light.svg create mode 100644 frontend/public/light/knife-light.svg create mode 100644 frontend/public/light/ladder-light.svg create mode 100644 frontend/public/light/ladder-simple-light.svg create mode 100644 frontend/public/light/lamp-light.svg create mode 100644 frontend/public/light/lamp-pendant-light.svg create mode 100644 frontend/public/light/laptop-light.svg create mode 100644 frontend/public/light/lasso-light.svg create mode 100644 frontend/public/light/lastfm-logo-light.svg create mode 100644 frontend/public/light/layout-light.svg create mode 100644 frontend/public/light/leaf-light.svg create mode 100644 frontend/public/light/lectern-light.svg create mode 100644 frontend/public/light/lego-light.svg create mode 100644 frontend/public/light/lego-smiley-light.svg create mode 100644 frontend/public/light/less-than-light.svg create mode 100644 frontend/public/light/less-than-or-equal-light.svg create mode 100644 frontend/public/light/letter-circle-h-light.svg create mode 100644 frontend/public/light/letter-circle-p-light.svg create mode 100644 frontend/public/light/letter-circle-v-light.svg create mode 100644 frontend/public/light/lifebuoy-light.svg create mode 100644 frontend/public/light/lightbulb-filament-light.svg create mode 100644 frontend/public/light/lightbulb-light.svg create mode 100644 frontend/public/light/lighthouse-light.svg create mode 100644 frontend/public/light/lightning-a-light.svg create mode 100644 frontend/public/light/lightning-light.svg create mode 100644 frontend/public/light/lightning-slash-light.svg create mode 100644 frontend/public/light/line-segment-light.svg create mode 100644 frontend/public/light/line-segments-light.svg create mode 100644 frontend/public/light/line-vertical-light.svg create mode 100644 frontend/public/light/link-break-light.svg create mode 100644 frontend/public/light/link-light.svg create mode 100644 frontend/public/light/link-simple-break-light.svg create mode 100644 frontend/public/light/link-simple-horizontal-break-light.svg create mode 100644 frontend/public/light/link-simple-horizontal-light.svg create mode 100644 frontend/public/light/link-simple-light.svg create mode 100644 frontend/public/light/linkedin-logo-light.svg create mode 100644 frontend/public/light/linktree-logo-light.svg create mode 100644 frontend/public/light/linux-logo-light.svg create mode 100644 frontend/public/light/list-bullets-light.svg create mode 100644 frontend/public/light/list-checks-light.svg create mode 100644 frontend/public/light/list-dashes-light.svg create mode 100644 frontend/public/light/list-heart-light.svg create mode 100644 frontend/public/light/list-light.svg create mode 100644 frontend/public/light/list-magnifying-glass-light.svg create mode 100644 frontend/public/light/list-numbers-light.svg create mode 100644 frontend/public/light/list-plus-light.svg create mode 100644 frontend/public/light/list-star-light.svg create mode 100644 frontend/public/light/lock-key-light.svg create mode 100644 frontend/public/light/lock-key-open-light.svg create mode 100644 frontend/public/light/lock-laminated-light.svg create mode 100644 frontend/public/light/lock-laminated-open-light.svg create mode 100644 frontend/public/light/lock-light.svg create mode 100644 frontend/public/light/lock-open-light.svg create mode 100644 frontend/public/light/lock-simple-light.svg create mode 100644 frontend/public/light/lock-simple-open-light.svg create mode 100644 frontend/public/light/lockers-light.svg create mode 100644 frontend/public/light/log-light.svg create mode 100644 frontend/public/light/magic-wand-light.svg create mode 100644 frontend/public/light/magnet-light.svg create mode 100644 frontend/public/light/magnet-straight-light.svg create mode 100644 frontend/public/light/magnifying-glass-light.svg create mode 100644 frontend/public/light/magnifying-glass-minus-light.svg create mode 100644 frontend/public/light/magnifying-glass-plus-light.svg create mode 100644 frontend/public/light/mailbox-light.svg create mode 100644 frontend/public/light/map-pin-area-light.svg create mode 100644 frontend/public/light/map-pin-light.svg create mode 100644 frontend/public/light/map-pin-line-light.svg create mode 100644 frontend/public/light/map-pin-plus-light.svg create mode 100644 frontend/public/light/map-pin-simple-area-light.svg create mode 100644 frontend/public/light/map-pin-simple-light.svg create mode 100644 frontend/public/light/map-pin-simple-line-light.svg create mode 100644 frontend/public/light/map-trifold-light.svg create mode 100644 frontend/public/light/markdown-logo-light.svg create mode 100644 frontend/public/light/marker-circle-light.svg create mode 100644 frontend/public/light/martini-light.svg create mode 100644 frontend/public/light/mask-happy-light.svg create mode 100644 frontend/public/light/mask-sad-light.svg create mode 100644 frontend/public/light/mastodon-logo-light.svg create mode 100644 frontend/public/light/math-operations-light.svg create mode 100644 frontend/public/light/matrix-logo-light.svg create mode 100644 frontend/public/light/medal-light.svg create mode 100644 frontend/public/light/medal-military-light.svg create mode 100644 frontend/public/light/medium-logo-light.svg create mode 100644 frontend/public/light/megaphone-light.svg create mode 100644 frontend/public/light/megaphone-simple-light.svg create mode 100644 frontend/public/light/member-of-light.svg create mode 100644 frontend/public/light/memory-light.svg create mode 100644 frontend/public/light/messenger-logo-light.svg create mode 100644 frontend/public/light/meta-logo-light.svg create mode 100644 frontend/public/light/meteor-light.svg create mode 100644 frontend/public/light/metronome-light.svg create mode 100644 frontend/public/light/microphone-light.svg create mode 100644 frontend/public/light/microphone-slash-light.svg create mode 100644 frontend/public/light/microphone-stage-light.svg create mode 100644 frontend/public/light/microscope-light.svg create mode 100644 frontend/public/light/microsoft-excel-logo-light.svg create mode 100644 frontend/public/light/microsoft-outlook-logo-light.svg create mode 100644 frontend/public/light/microsoft-powerpoint-logo-light.svg create mode 100644 frontend/public/light/microsoft-teams-logo-light.svg create mode 100644 frontend/public/light/microsoft-word-logo-light.svg create mode 100644 frontend/public/light/minus-circle-light.svg create mode 100644 frontend/public/light/minus-light.svg create mode 100644 frontend/public/light/minus-square-light.svg create mode 100644 frontend/public/light/money-light.svg create mode 100644 frontend/public/light/money-wavy-light.svg create mode 100644 frontend/public/light/monitor-arrow-up-light.svg create mode 100644 frontend/public/light/monitor-light.svg create mode 100644 frontend/public/light/monitor-play-light.svg create mode 100644 frontend/public/light/moon-light.svg create mode 100644 frontend/public/light/moon-stars-light.svg create mode 100644 frontend/public/light/moped-front-light.svg create mode 100644 frontend/public/light/moped-light.svg create mode 100644 frontend/public/light/mosque-light.svg create mode 100644 frontend/public/light/motorcycle-light.svg create mode 100644 frontend/public/light/mountains-light.svg create mode 100644 frontend/public/light/mouse-left-click-light.svg create mode 100644 frontend/public/light/mouse-light.svg create mode 100644 frontend/public/light/mouse-middle-click-light.svg create mode 100644 frontend/public/light/mouse-right-click-light.svg create mode 100644 frontend/public/light/mouse-scroll-light.svg create mode 100644 frontend/public/light/mouse-simple-light.svg create mode 100644 frontend/public/light/music-note-light.svg create mode 100644 frontend/public/light/music-note-simple-light.svg create mode 100644 frontend/public/light/music-notes-light.svg create mode 100644 frontend/public/light/music-notes-minus-light.svg create mode 100644 frontend/public/light/music-notes-plus-light.svg create mode 100644 frontend/public/light/music-notes-simple-light.svg create mode 100644 frontend/public/light/navigation-arrow-light.svg create mode 100644 frontend/public/light/needle-light.svg create mode 100644 frontend/public/light/network-light.svg create mode 100644 frontend/public/light/network-slash-light.svg create mode 100644 frontend/public/light/network-x-light.svg create mode 100644 frontend/public/light/newspaper-clipping-light.svg create mode 100644 frontend/public/light/newspaper-light.svg create mode 100644 frontend/public/light/not-equals-light.svg create mode 100644 frontend/public/light/not-member-of-light.svg create mode 100644 frontend/public/light/not-subset-of-light.svg create mode 100644 frontend/public/light/not-superset-of-light.svg create mode 100644 frontend/public/light/notches-light.svg create mode 100644 frontend/public/light/note-blank-light.svg create mode 100644 frontend/public/light/note-light.svg create mode 100644 frontend/public/light/note-pencil-light.svg create mode 100644 frontend/public/light/notebook-light.svg create mode 100644 frontend/public/light/notepad-light.svg create mode 100644 frontend/public/light/notification-light.svg create mode 100644 frontend/public/light/notion-logo-light.svg create mode 100644 frontend/public/light/nuclear-plant-light.svg create mode 100644 frontend/public/light/number-circle-eight-light.svg create mode 100644 frontend/public/light/number-circle-five-light.svg create mode 100644 frontend/public/light/number-circle-four-light.svg create mode 100644 frontend/public/light/number-circle-nine-light.svg create mode 100644 frontend/public/light/number-circle-one-light.svg create mode 100644 frontend/public/light/number-circle-seven-light.svg create mode 100644 frontend/public/light/number-circle-six-light.svg create mode 100644 frontend/public/light/number-circle-three-light.svg create mode 100644 frontend/public/light/number-circle-two-light.svg create mode 100644 frontend/public/light/number-circle-zero-light.svg create mode 100644 frontend/public/light/number-eight-light.svg create mode 100644 frontend/public/light/number-five-light.svg create mode 100644 frontend/public/light/number-four-light.svg create mode 100644 frontend/public/light/number-nine-light.svg create mode 100644 frontend/public/light/number-one-light.svg create mode 100644 frontend/public/light/number-seven-light.svg create mode 100644 frontend/public/light/number-six-light.svg create mode 100644 frontend/public/light/number-square-eight-light.svg create mode 100644 frontend/public/light/number-square-five-light.svg create mode 100644 frontend/public/light/number-square-four-light.svg create mode 100644 frontend/public/light/number-square-nine-light.svg create mode 100644 frontend/public/light/number-square-one-light.svg create mode 100644 frontend/public/light/number-square-seven-light.svg create mode 100644 frontend/public/light/number-square-six-light.svg create mode 100644 frontend/public/light/number-square-three-light.svg create mode 100644 frontend/public/light/number-square-two-light.svg create mode 100644 frontend/public/light/number-square-zero-light.svg create mode 100644 frontend/public/light/number-three-light.svg create mode 100644 frontend/public/light/number-two-light.svg create mode 100644 frontend/public/light/number-zero-light.svg create mode 100644 frontend/public/light/numpad-light.svg create mode 100644 frontend/public/light/nut-light.svg create mode 100644 frontend/public/light/ny-times-logo-light.svg create mode 100644 frontend/public/light/octagon-light.svg create mode 100644 frontend/public/light/office-chair-light.svg create mode 100644 frontend/public/light/onigiri-light.svg create mode 100644 frontend/public/light/open-ai-logo-light.svg create mode 100644 frontend/public/light/option-light.svg create mode 100644 frontend/public/light/orange-light.svg create mode 100644 frontend/public/light/orange-slice-light.svg create mode 100644 frontend/public/light/oven-light.svg create mode 100644 frontend/public/light/package-light.svg create mode 100644 frontend/public/light/paint-brush-broad-light.svg create mode 100644 frontend/public/light/paint-brush-household-light.svg create mode 100644 frontend/public/light/paint-brush-light.svg create mode 100644 frontend/public/light/paint-bucket-light.svg create mode 100644 frontend/public/light/paint-roller-light.svg create mode 100644 frontend/public/light/palette-light.svg create mode 100644 frontend/public/light/panorama-light.svg create mode 100644 frontend/public/light/pants-light.svg create mode 100644 frontend/public/light/paper-plane-light.svg create mode 100644 frontend/public/light/paper-plane-right-light.svg create mode 100644 frontend/public/light/paper-plane-tilt-light.svg create mode 100644 frontend/public/light/paperclip-horizontal-light.svg create mode 100644 frontend/public/light/paperclip-light.svg create mode 100644 frontend/public/light/parachute-light.svg create mode 100644 frontend/public/light/paragraph-light.svg create mode 100644 frontend/public/light/parallelogram-light.svg create mode 100644 frontend/public/light/park-light.svg create mode 100644 frontend/public/light/password-light.svg create mode 100644 frontend/public/light/path-light.svg create mode 100644 frontend/public/light/patreon-logo-light.svg create mode 100644 frontend/public/light/pause-circle-light.svg create mode 100644 frontend/public/light/pause-light.svg create mode 100644 frontend/public/light/paw-print-light.svg create mode 100644 frontend/public/light/paypal-logo-light.svg create mode 100644 frontend/public/light/peace-light.svg create mode 100644 frontend/public/light/pen-light.svg create mode 100644 frontend/public/light/pen-nib-light.svg create mode 100644 frontend/public/light/pen-nib-straight-light.svg create mode 100644 frontend/public/light/pencil-circle-light.svg create mode 100644 frontend/public/light/pencil-light.svg create mode 100644 frontend/public/light/pencil-line-light.svg create mode 100644 frontend/public/light/pencil-ruler-light.svg create mode 100644 frontend/public/light/pencil-simple-light.svg create mode 100644 frontend/public/light/pencil-simple-line-light.svg create mode 100644 frontend/public/light/pencil-simple-slash-light.svg create mode 100644 frontend/public/light/pencil-slash-light.svg create mode 100644 frontend/public/light/pentagon-light.svg create mode 100644 frontend/public/light/pentagram-light.svg create mode 100644 frontend/public/light/pepper-light.svg create mode 100644 frontend/public/light/percent-light.svg create mode 100644 frontend/public/light/person-arms-spread-light.svg create mode 100644 frontend/public/light/person-light.svg create mode 100644 frontend/public/light/person-simple-bike-light.svg create mode 100644 frontend/public/light/person-simple-circle-light.svg create mode 100644 frontend/public/light/person-simple-hike-light.svg create mode 100644 frontend/public/light/person-simple-light.svg create mode 100644 frontend/public/light/person-simple-run-light.svg create mode 100644 frontend/public/light/person-simple-ski-light.svg create mode 100644 frontend/public/light/person-simple-snowboard-light.svg create mode 100644 frontend/public/light/person-simple-swim-light.svg create mode 100644 frontend/public/light/person-simple-tai-chi-light.svg create mode 100644 frontend/public/light/person-simple-throw-light.svg create mode 100644 frontend/public/light/person-simple-walk-light.svg create mode 100644 frontend/public/light/perspective-light.svg create mode 100644 frontend/public/light/phone-call-light.svg create mode 100644 frontend/public/light/phone-disconnect-light.svg create mode 100644 frontend/public/light/phone-incoming-light.svg create mode 100644 frontend/public/light/phone-light.svg create mode 100644 frontend/public/light/phone-list-light.svg create mode 100644 frontend/public/light/phone-outgoing-light.svg create mode 100644 frontend/public/light/phone-pause-light.svg create mode 100644 frontend/public/light/phone-plus-light.svg create mode 100644 frontend/public/light/phone-slash-light.svg create mode 100644 frontend/public/light/phone-transfer-light.svg create mode 100644 frontend/public/light/phone-x-light.svg create mode 100644 frontend/public/light/phosphor-logo-light.svg create mode 100644 frontend/public/light/pi-light.svg create mode 100644 frontend/public/light/piano-keys-light.svg create mode 100644 frontend/public/light/picnic-table-light.svg create mode 100644 frontend/public/light/picture-in-picture-light.svg create mode 100644 frontend/public/light/piggy-bank-light.svg create mode 100644 frontend/public/light/pill-light.svg create mode 100644 frontend/public/light/ping-pong-light.svg create mode 100644 frontend/public/light/pint-glass-light.svg create mode 100644 frontend/public/light/pinterest-logo-light.svg create mode 100644 frontend/public/light/pinwheel-light.svg create mode 100644 frontend/public/light/pipe-light.svg create mode 100644 frontend/public/light/pipe-wrench-light.svg create mode 100644 frontend/public/light/pix-logo-light.svg create mode 100644 frontend/public/light/pizza-light.svg create mode 100644 frontend/public/light/placeholder-light.svg create mode 100644 frontend/public/light/planet-light.svg create mode 100644 frontend/public/light/plant-light.svg create mode 100644 frontend/public/light/play-circle-light.svg create mode 100644 frontend/public/light/play-light.svg create mode 100644 frontend/public/light/play-pause-light.svg create mode 100644 frontend/public/light/playlist-light.svg create mode 100644 frontend/public/light/plug-charging-light.svg create mode 100644 frontend/public/light/plug-light.svg create mode 100644 frontend/public/light/plugs-connected-light.svg create mode 100644 frontend/public/light/plugs-light.svg create mode 100644 frontend/public/light/plus-circle-light.svg create mode 100644 frontend/public/light/plus-light.svg create mode 100644 frontend/public/light/plus-minus-light.svg create mode 100644 frontend/public/light/plus-square-light.svg create mode 100644 frontend/public/light/poker-chip-light.svg create mode 100644 frontend/public/light/police-car-light.svg create mode 100644 frontend/public/light/polygon-light.svg create mode 100644 frontend/public/light/popcorn-light.svg create mode 100644 frontend/public/light/popsicle-light.svg create mode 100644 frontend/public/light/potted-plant-light.svg create mode 100644 frontend/public/light/power-light.svg create mode 100644 frontend/public/light/prescription-light.svg create mode 100644 frontend/public/light/presentation-chart-light.svg create mode 100644 frontend/public/light/presentation-light.svg create mode 100644 frontend/public/light/printer-light.svg create mode 100644 frontend/public/light/prohibit-inset-light.svg create mode 100644 frontend/public/light/prohibit-light.svg create mode 100644 frontend/public/light/projector-screen-chart-light.svg create mode 100644 frontend/public/light/projector-screen-light.svg create mode 100644 frontend/public/light/pulse-light.svg create mode 100644 frontend/public/light/push-pin-light.svg create mode 100644 frontend/public/light/push-pin-simple-light.svg create mode 100644 frontend/public/light/push-pin-simple-slash-light.svg create mode 100644 frontend/public/light/push-pin-slash-light.svg create mode 100644 frontend/public/light/puzzle-piece-light.svg create mode 100644 frontend/public/light/qr-code-light.svg create mode 100644 frontend/public/light/question-light.svg create mode 100644 frontend/public/light/question-mark-light.svg create mode 100644 frontend/public/light/queue-light.svg create mode 100644 frontend/public/light/quotes-light.svg create mode 100644 frontend/public/light/rabbit-light.svg create mode 100644 frontend/public/light/racquet-light.svg create mode 100644 frontend/public/light/radical-light.svg create mode 100644 frontend/public/light/radio-button-light.svg create mode 100644 frontend/public/light/radio-light.svg create mode 100644 frontend/public/light/radioactive-light.svg create mode 100644 frontend/public/light/rainbow-cloud-light.svg create mode 100644 frontend/public/light/rainbow-light.svg create mode 100644 frontend/public/light/ranking-light.svg create mode 100644 frontend/public/light/read-cv-logo-light.svg create mode 100644 frontend/public/light/receipt-light.svg create mode 100644 frontend/public/light/receipt-x-light.svg create mode 100644 frontend/public/light/record-light.svg create mode 100644 frontend/public/light/rectangle-dashed-light.svg create mode 100644 frontend/public/light/rectangle-light.svg create mode 100644 frontend/public/light/recycle-light.svg create mode 100644 frontend/public/light/reddit-logo-light.svg create mode 100644 frontend/public/light/repeat-light.svg create mode 100644 frontend/public/light/repeat-once-light.svg create mode 100644 frontend/public/light/replit-logo-light.svg create mode 100644 frontend/public/light/resize-light.svg create mode 100644 frontend/public/light/rewind-circle-light.svg create mode 100644 frontend/public/light/rewind-light.svg create mode 100644 frontend/public/light/road-horizon-light.svg create mode 100644 frontend/public/light/robot-light.svg create mode 100644 frontend/public/light/rocket-launch-light.svg create mode 100644 frontend/public/light/rocket-light.svg create mode 100644 frontend/public/light/rows-light.svg create mode 100644 frontend/public/light/rows-plus-bottom-light.svg create mode 100644 frontend/public/light/rows-plus-top-light.svg create mode 100644 frontend/public/light/rss-light.svg create mode 100644 frontend/public/light/rss-simple-light.svg create mode 100644 frontend/public/light/rug-light.svg create mode 100644 frontend/public/light/ruler-light.svg create mode 100644 frontend/public/light/sailboat-light.svg create mode 100644 frontend/public/light/scales-light.svg create mode 100644 frontend/public/light/scan-light.svg create mode 100644 frontend/public/light/scan-smiley-light.svg create mode 100644 frontend/public/light/scissors-light.svg create mode 100644 frontend/public/light/scooter-light.svg create mode 100644 frontend/public/light/screencast-light.svg create mode 100644 frontend/public/light/screwdriver-light.svg create mode 100644 frontend/public/light/scribble-light.svg create mode 100644 frontend/public/light/scribble-loop-light.svg create mode 100644 frontend/public/light/scroll-light.svg create mode 100644 frontend/public/light/seal-check-light.svg create mode 100644 frontend/public/light/seal-light.svg create mode 100644 frontend/public/light/seal-percent-light.svg create mode 100644 frontend/public/light/seal-question-light.svg create mode 100644 frontend/public/light/seal-warning-light.svg create mode 100644 frontend/public/light/seat-light.svg create mode 100644 frontend/public/light/seatbelt-light.svg create mode 100644 frontend/public/light/security-camera-light.svg create mode 100644 frontend/public/light/selection-all-light.svg create mode 100644 frontend/public/light/selection-background-light.svg create mode 100644 frontend/public/light/selection-foreground-light.svg create mode 100644 frontend/public/light/selection-inverse-light.svg create mode 100644 frontend/public/light/selection-light.svg create mode 100644 frontend/public/light/selection-plus-light.svg create mode 100644 frontend/public/light/selection-slash-light.svg create mode 100644 frontend/public/light/shapes-light.svg create mode 100644 frontend/public/light/share-fat-light.svg create mode 100644 frontend/public/light/share-light.svg create mode 100644 frontend/public/light/share-network-light.svg create mode 100644 frontend/public/light/shield-check-light.svg create mode 100644 frontend/public/light/shield-checkered-light.svg create mode 100644 frontend/public/light/shield-chevron-light.svg create mode 100644 frontend/public/light/shield-light.svg create mode 100644 frontend/public/light/shield-plus-light.svg create mode 100644 frontend/public/light/shield-slash-light.svg create mode 100644 frontend/public/light/shield-star-light.svg create mode 100644 frontend/public/light/shield-warning-light.svg create mode 100644 frontend/public/light/shipping-container-light.svg create mode 100644 frontend/public/light/shirt-folded-light.svg create mode 100644 frontend/public/light/shooting-star-light.svg create mode 100644 frontend/public/light/shopping-bag-light.svg create mode 100644 frontend/public/light/shopping-bag-open-light.svg create mode 100644 frontend/public/light/shopping-cart-light.svg create mode 100644 frontend/public/light/shopping-cart-simple-light.svg create mode 100644 frontend/public/light/shovel-light.svg create mode 100644 frontend/public/light/shower-light.svg create mode 100644 frontend/public/light/shrimp-light.svg create mode 100644 frontend/public/light/shuffle-angular-light.svg create mode 100644 frontend/public/light/shuffle-light.svg create mode 100644 frontend/public/light/shuffle-simple-light.svg create mode 100644 frontend/public/light/sidebar-light.svg create mode 100644 frontend/public/light/sidebar-simple-light.svg create mode 100644 frontend/public/light/sigma-light.svg create mode 100644 frontend/public/light/sign-in-light.svg create mode 100644 frontend/public/light/sign-out-light.svg create mode 100644 frontend/public/light/signature-light.svg create mode 100644 frontend/public/light/signpost-light.svg create mode 100644 frontend/public/light/sim-card-light.svg create mode 100644 frontend/public/light/siren-light.svg create mode 100644 frontend/public/light/sketch-logo-light.svg create mode 100644 frontend/public/light/skip-back-circle-light.svg create mode 100644 frontend/public/light/skip-back-light.svg create mode 100644 frontend/public/light/skip-forward-circle-light.svg create mode 100644 frontend/public/light/skip-forward-light.svg create mode 100644 frontend/public/light/skull-light.svg create mode 100644 frontend/public/light/skype-logo-light.svg create mode 100644 frontend/public/light/slack-logo-light.svg create mode 100644 frontend/public/light/sliders-horizontal-light.svg create mode 100644 frontend/public/light/sliders-light.svg create mode 100644 frontend/public/light/slideshow-light.svg create mode 100644 frontend/public/light/smiley-angry-light.svg create mode 100644 frontend/public/light/smiley-blank-light.svg create mode 100644 frontend/public/light/smiley-light.svg create mode 100644 frontend/public/light/smiley-meh-light.svg create mode 100644 frontend/public/light/smiley-melting-light.svg create mode 100644 frontend/public/light/smiley-nervous-light.svg create mode 100644 frontend/public/light/smiley-sad-light.svg create mode 100644 frontend/public/light/smiley-sticker-light.svg create mode 100644 frontend/public/light/smiley-wink-light.svg create mode 100644 frontend/public/light/smiley-x-eyes-light.svg create mode 100644 frontend/public/light/snapchat-logo-light.svg create mode 100644 frontend/public/light/sneaker-light.svg create mode 100644 frontend/public/light/sneaker-move-light.svg create mode 100644 frontend/public/light/snowflake-light.svg create mode 100644 frontend/public/light/soccer-ball-light.svg create mode 100644 frontend/public/light/sock-light.svg create mode 100644 frontend/public/light/solar-panel-light.svg create mode 100644 frontend/public/light/solar-roof-light.svg create mode 100644 frontend/public/light/sort-ascending-light.svg create mode 100644 frontend/public/light/sort-descending-light.svg create mode 100644 frontend/public/light/soundcloud-logo-light.svg create mode 100644 frontend/public/light/spade-light.svg create mode 100644 frontend/public/light/sparkle-light.svg create mode 100644 frontend/public/light/speaker-hifi-light.svg create mode 100644 frontend/public/light/speaker-high-light.svg create mode 100644 frontend/public/light/speaker-low-light.svg create mode 100644 frontend/public/light/speaker-none-light.svg create mode 100644 frontend/public/light/speaker-simple-high-light.svg create mode 100644 frontend/public/light/speaker-simple-low-light.svg create mode 100644 frontend/public/light/speaker-simple-none-light.svg create mode 100644 frontend/public/light/speaker-simple-slash-light.svg create mode 100644 frontend/public/light/speaker-simple-x-light.svg create mode 100644 frontend/public/light/speaker-slash-light.svg create mode 100644 frontend/public/light/speaker-x-light.svg create mode 100644 frontend/public/light/speedometer-light.svg create mode 100644 frontend/public/light/sphere-light.svg create mode 100644 frontend/public/light/spinner-ball-light.svg create mode 100644 frontend/public/light/spinner-gap-light.svg create mode 100644 frontend/public/light/spinner-light.svg create mode 100644 frontend/public/light/spiral-light.svg create mode 100644 frontend/public/light/split-horizontal-light.svg create mode 100644 frontend/public/light/split-vertical-light.svg create mode 100644 frontend/public/light/spotify-logo-light.svg create mode 100644 frontend/public/light/spray-bottle-light.svg create mode 100644 frontend/public/light/square-half-bottom-light.svg create mode 100644 frontend/public/light/square-half-light.svg create mode 100644 frontend/public/light/square-light.svg create mode 100644 frontend/public/light/square-logo-light.svg create mode 100644 frontend/public/light/square-split-horizontal-light.svg create mode 100644 frontend/public/light/square-split-vertical-light.svg create mode 100644 frontend/public/light/squares-four-light.svg create mode 100644 frontend/public/light/stack-light.svg create mode 100644 frontend/public/light/stack-minus-light.svg create mode 100644 frontend/public/light/stack-overflow-logo-light.svg create mode 100644 frontend/public/light/stack-plus-light.svg create mode 100644 frontend/public/light/stack-simple-light.svg create mode 100644 frontend/public/light/stairs-light.svg create mode 100644 frontend/public/light/stamp-light.svg create mode 100644 frontend/public/light/standard-definition-light.svg create mode 100644 frontend/public/light/star-and-crescent-light.svg create mode 100644 frontend/public/light/star-four-light.svg create mode 100644 frontend/public/light/star-half-light.svg create mode 100644 frontend/public/light/star-light.svg create mode 100644 frontend/public/light/star-of-david-light.svg create mode 100644 frontend/public/light/steam-logo-light.svg create mode 100644 frontend/public/light/steering-wheel-light.svg create mode 100644 frontend/public/light/steps-light.svg create mode 100644 frontend/public/light/stethoscope-light.svg create mode 100644 frontend/public/light/sticker-light.svg create mode 100644 frontend/public/light/stool-light.svg create mode 100644 frontend/public/light/stop-circle-light.svg create mode 100644 frontend/public/light/stop-light.svg create mode 100644 frontend/public/light/storefront-light.svg create mode 100644 frontend/public/light/strategy-light.svg create mode 100644 frontend/public/light/stripe-logo-light.svg create mode 100644 frontend/public/light/student-light.svg create mode 100644 frontend/public/light/subset-of-light.svg create mode 100644 frontend/public/light/subset-proper-of-light.svg create mode 100644 frontend/public/light/subtitles-light.svg create mode 100644 frontend/public/light/subtitles-slash-light.svg create mode 100644 frontend/public/light/subtract-light.svg create mode 100644 frontend/public/light/subtract-square-light.svg create mode 100644 frontend/public/light/subway-light.svg create mode 100644 frontend/public/light/suitcase-light.svg create mode 100644 frontend/public/light/suitcase-rolling-light.svg create mode 100644 frontend/public/light/suitcase-simple-light.svg create mode 100644 frontend/public/light/sun-dim-light.svg create mode 100644 frontend/public/light/sun-horizon-light.svg create mode 100644 frontend/public/light/sun-light.svg create mode 100644 frontend/public/light/sunglasses-light.svg create mode 100644 frontend/public/light/superset-of-light.svg create mode 100644 frontend/public/light/superset-proper-of-light.svg create mode 100644 frontend/public/light/swap-light.svg create mode 100644 frontend/public/light/swatches-light.svg create mode 100644 frontend/public/light/swimming-pool-light.svg create mode 100644 frontend/public/light/sword-light.svg create mode 100644 frontend/public/light/synagogue-light.svg create mode 100644 frontend/public/light/syringe-light.svg create mode 100644 frontend/public/light/t-shirt-light.svg create mode 100644 frontend/public/light/table-light.svg create mode 100644 frontend/public/light/tabs-light.svg create mode 100644 frontend/public/light/tag-chevron-light.svg create mode 100644 frontend/public/light/tag-light.svg create mode 100644 frontend/public/light/tag-simple-light.svg create mode 100644 frontend/public/light/target-light.svg create mode 100644 frontend/public/light/taxi-light.svg create mode 100644 frontend/public/light/tea-bag-light.svg create mode 100644 frontend/public/light/telegram-logo-light.svg create mode 100644 frontend/public/light/television-light.svg create mode 100644 frontend/public/light/television-simple-light.svg create mode 100644 frontend/public/light/tennis-ball-light.svg create mode 100644 frontend/public/light/tent-light.svg create mode 100644 frontend/public/light/terminal-light.svg create mode 100644 frontend/public/light/terminal-window-light.svg create mode 100644 frontend/public/light/test-tube-light.svg create mode 100644 frontend/public/light/text-a-underline-light.svg create mode 100644 frontend/public/light/text-aa-light.svg create mode 100644 frontend/public/light/text-align-center-light.svg create mode 100644 frontend/public/light/text-align-justify-light.svg create mode 100644 frontend/public/light/text-align-left-light.svg create mode 100644 frontend/public/light/text-align-right-light.svg create mode 100644 frontend/public/light/text-b-light.svg create mode 100644 frontend/public/light/text-columns-light.svg create mode 100644 frontend/public/light/text-h-five-light.svg create mode 100644 frontend/public/light/text-h-four-light.svg create mode 100644 frontend/public/light/text-h-light.svg create mode 100644 frontend/public/light/text-h-one-light.svg create mode 100644 frontend/public/light/text-h-six-light.svg create mode 100644 frontend/public/light/text-h-three-light.svg create mode 100644 frontend/public/light/text-h-two-light.svg create mode 100644 frontend/public/light/text-indent-light.svg create mode 100644 frontend/public/light/text-italic-light.svg create mode 100644 frontend/public/light/text-outdent-light.svg create mode 100644 frontend/public/light/text-strikethrough-light.svg create mode 100644 frontend/public/light/text-subscript-light.svg create mode 100644 frontend/public/light/text-superscript-light.svg create mode 100644 frontend/public/light/text-t-light.svg create mode 100644 frontend/public/light/text-t-slash-light.svg create mode 100644 frontend/public/light/text-underline-light.svg create mode 100644 frontend/public/light/textbox-light.svg create mode 100644 frontend/public/light/thermometer-cold-light.svg create mode 100644 frontend/public/light/thermometer-hot-light.svg create mode 100644 frontend/public/light/thermometer-light.svg create mode 100644 frontend/public/light/thermometer-simple-light.svg create mode 100644 frontend/public/light/threads-logo-light.svg create mode 100644 frontend/public/light/three-d-light.svg create mode 100644 frontend/public/light/thumbs-down-light.svg create mode 100644 frontend/public/light/thumbs-up-light.svg create mode 100644 frontend/public/light/ticket-light.svg create mode 100644 frontend/public/light/tidal-logo-light.svg create mode 100644 frontend/public/light/tiktok-logo-light.svg create mode 100644 frontend/public/light/tilde-light.svg create mode 100644 frontend/public/light/timer-light.svg create mode 100644 frontend/public/light/tip-jar-light.svg create mode 100644 frontend/public/light/tipi-light.svg create mode 100644 frontend/public/light/tire-light.svg create mode 100644 frontend/public/light/toggle-left-light.svg create mode 100644 frontend/public/light/toggle-right-light.svg create mode 100644 frontend/public/light/toilet-light.svg create mode 100644 frontend/public/light/toilet-paper-light.svg create mode 100644 frontend/public/light/toolbox-light.svg create mode 100644 frontend/public/light/tooth-light.svg create mode 100644 frontend/public/light/tornado-light.svg create mode 100644 frontend/public/light/tote-light.svg create mode 100644 frontend/public/light/tote-simple-light.svg create mode 100644 frontend/public/light/towel-light.svg create mode 100644 frontend/public/light/tractor-light.svg create mode 100644 frontend/public/light/trademark-light.svg create mode 100644 frontend/public/light/trademark-registered-light.svg create mode 100644 frontend/public/light/traffic-cone-light.svg create mode 100644 frontend/public/light/traffic-sign-light.svg create mode 100644 frontend/public/light/traffic-signal-light.svg create mode 100644 frontend/public/light/train-light.svg create mode 100644 frontend/public/light/train-regional-light.svg create mode 100644 frontend/public/light/train-simple-light.svg create mode 100644 frontend/public/light/tram-light.svg create mode 100644 frontend/public/light/translate-light.svg create mode 100644 frontend/public/light/trash-light.svg create mode 100644 frontend/public/light/trash-simple-light.svg create mode 100644 frontend/public/light/tray-arrow-down-light.svg create mode 100644 frontend/public/light/tray-arrow-up-light.svg create mode 100644 frontend/public/light/tray-light.svg create mode 100644 frontend/public/light/treasure-chest-light.svg create mode 100644 frontend/public/light/tree-evergreen-light.svg create mode 100644 frontend/public/light/tree-light.svg create mode 100644 frontend/public/light/tree-palm-light.svg create mode 100644 frontend/public/light/tree-structure-light.svg create mode 100644 frontend/public/light/tree-view-light.svg create mode 100644 frontend/public/light/trend-down-light.svg create mode 100644 frontend/public/light/trend-up-light.svg create mode 100644 frontend/public/light/triangle-dashed-light.svg create mode 100644 frontend/public/light/triangle-light.svg create mode 100644 frontend/public/light/trolley-light.svg create mode 100644 frontend/public/light/trolley-suitcase-light.svg create mode 100644 frontend/public/light/trophy-light.svg create mode 100644 frontend/public/light/truck-light.svg create mode 100644 frontend/public/light/truck-trailer-light.svg create mode 100644 frontend/public/light/tumblr-logo-light.svg create mode 100644 frontend/public/light/twitch-logo-light.svg create mode 100644 frontend/public/light/twitter-logo-light.svg create mode 100644 frontend/public/light/umbrella-light.svg create mode 100644 frontend/public/light/umbrella-simple-light.svg create mode 100644 frontend/public/light/union-light.svg create mode 100644 frontend/public/light/unite-light.svg create mode 100644 frontend/public/light/unite-square-light.svg create mode 100644 frontend/public/light/upload-light.svg create mode 100644 frontend/public/light/upload-simple-light.svg create mode 100644 frontend/public/light/usb-light.svg create mode 100644 frontend/public/light/user-check-light.svg create mode 100644 frontend/public/light/user-circle-check-light.svg create mode 100644 frontend/public/light/user-circle-dashed-light.svg create mode 100644 frontend/public/light/user-circle-gear-light.svg create mode 100644 frontend/public/light/user-circle-light.svg create mode 100644 frontend/public/light/user-circle-minus-light.svg create mode 100644 frontend/public/light/user-circle-plus-light.svg create mode 100644 frontend/public/light/user-focus-light.svg create mode 100644 frontend/public/light/user-gear-light.svg create mode 100644 frontend/public/light/user-light.svg create mode 100644 frontend/public/light/user-list-light.svg create mode 100644 frontend/public/light/user-minus-light.svg create mode 100644 frontend/public/light/user-plus-light.svg create mode 100644 frontend/public/light/user-rectangle-light.svg create mode 100644 frontend/public/light/user-sound-light.svg create mode 100644 frontend/public/light/user-square-light.svg create mode 100644 frontend/public/light/user-switch-light.svg create mode 100644 frontend/public/light/users-four-light.svg create mode 100644 frontend/public/light/users-light.svg create mode 100644 frontend/public/light/users-three-light.svg create mode 100644 frontend/public/light/van-light.svg create mode 100644 frontend/public/light/vault-light.svg create mode 100644 frontend/public/light/vector-three-light.svg create mode 100644 frontend/public/light/vector-two-light.svg create mode 100644 frontend/public/light/vibrate-light.svg create mode 100644 frontend/public/light/video-camera-light.svg create mode 100644 frontend/public/light/video-camera-slash-light.svg create mode 100644 frontend/public/light/video-conference-light.svg create mode 100644 frontend/public/light/video-light.svg create mode 100644 frontend/public/light/vignette-light.svg create mode 100644 frontend/public/light/vinyl-record-light.svg create mode 100644 frontend/public/light/virtual-reality-light.svg create mode 100644 frontend/public/light/virus-light.svg create mode 100644 frontend/public/light/visor-light.svg create mode 100644 frontend/public/light/voicemail-light.svg create mode 100644 frontend/public/light/volleyball-light.svg create mode 100644 frontend/public/light/wall-light.svg create mode 100644 frontend/public/light/wallet-light.svg create mode 100644 frontend/public/light/warehouse-light.svg create mode 100644 frontend/public/light/warning-circle-light.svg create mode 100644 frontend/public/light/warning-diamond-light.svg create mode 100644 frontend/public/light/warning-light.svg create mode 100644 frontend/public/light/warning-octagon-light.svg create mode 100644 frontend/public/light/washing-machine-light.svg create mode 100644 frontend/public/light/watch-light.svg create mode 100644 frontend/public/light/wave-sawtooth-light.svg create mode 100644 frontend/public/light/wave-sine-light.svg create mode 100644 frontend/public/light/wave-square-light.svg create mode 100644 frontend/public/light/wave-triangle-light.svg create mode 100644 frontend/public/light/waveform-light.svg create mode 100644 frontend/public/light/waveform-slash-light.svg create mode 100644 frontend/public/light/waves-light.svg create mode 100644 frontend/public/light/webcam-light.svg create mode 100644 frontend/public/light/webcam-slash-light.svg create mode 100644 frontend/public/light/webhooks-logo-light.svg create mode 100644 frontend/public/light/wechat-logo-light.svg create mode 100644 frontend/public/light/whatsapp-logo-light.svg create mode 100644 frontend/public/light/wheelchair-light.svg create mode 100644 frontend/public/light/wheelchair-motion-light.svg create mode 100644 frontend/public/light/wifi-high-light.svg create mode 100644 frontend/public/light/wifi-low-light.svg create mode 100644 frontend/public/light/wifi-medium-light.svg create mode 100644 frontend/public/light/wifi-none-light.svg create mode 100644 frontend/public/light/wifi-slash-light.svg create mode 100644 frontend/public/light/wifi-x-light.svg create mode 100644 frontend/public/light/wind-light.svg create mode 100644 frontend/public/light/windmill-light.svg create mode 100644 frontend/public/light/windows-logo-light.svg create mode 100644 frontend/public/light/wine-light.svg create mode 100644 frontend/public/light/wrench-light.svg create mode 100644 frontend/public/light/x-circle-light.svg create mode 100644 frontend/public/light/x-light.svg create mode 100644 frontend/public/light/x-logo-light.svg create mode 100644 frontend/public/light/x-square-light.svg create mode 100644 frontend/public/light/yarn-light.svg create mode 100644 frontend/public/light/yin-yang-light.svg create mode 100644 frontend/public/light/youtube-logo-light.svg create mode 100644 frontend/public/next.svg create mode 100644 frontend/public/vercel.svg create mode 100644 frontend/public/waras.webp create mode 100644 frontend/public/waras_io/android-chrome-192x192.png create mode 100644 frontend/public/waras_io/android-chrome-512x512.png create mode 100644 frontend/public/waras_io/apple-touch-icon.png create mode 100644 frontend/public/waras_io/favicon-16x16.png create mode 100644 frontend/public/waras_io/favicon-32x32.png create mode 100644 frontend/public/waras_io/favicon.ico create mode 100644 frontend/public/waras_io/site.webmanifest create mode 100644 frontend/public/window.svg create mode 100644 frontend/tsconfig.json create mode 100644 fuzzy-service/Dockerfile create mode 100644 fuzzy-service/fuzzy/__init__.py create mode 100644 fuzzy-service/fuzzy/engine.py create mode 100644 fuzzy-service/requirements.txt create mode 100644 fuzzy-service/service/__init__.py create mode 100644 fuzzy-service/service/main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cfc4679 --- /dev/null +++ b/.gitignore @@ -0,0 +1,53 @@ +# Dependencies +node_modules/ +.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# Next.js Build Output +.next/ +out/ +build/ + +# Python Cache +__pycache__/ +*.pyc +*.pyo +*.pyd +.venv/ +env/ +venv/ +ENV/ +.pytest_cache/ +.coverage +htmlcov/ + +# Logs and system files +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* +.DS_Store +Thumbs.db + +# Environmental variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local +*.pem + +# Editor/IDE configs +.idea/ +.vscode/ +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000..c01bbcd --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,4 @@ +PORT=4000 +JWT_SECRET=replace-with-secure-random-secret +FRONTEND_URL=http://localhost:3000 +DATABASE_URL=postgres://sig:sig_secret@localhost:5432/sig diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..058f3db --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,12 @@ +FROM node:20-alpine + +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci --omit=dev + +COPY src ./src + +EXPOSE 4000 + +CMD ["node", "src/server.js"] diff --git a/backend/bun.lock b/backend/bun.lock new file mode 100644 index 0000000..373b673 --- /dev/null +++ b/backend/bun.lock @@ -0,0 +1,218 @@ +{ + "lockfileVersion": 1, + "configVersion": 0, + "workspaces": { + "": { + "name": "sig-backend", + "dependencies": { + "bcryptjs": "^2.4.3", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.18.2", + "jsonwebtoken": "^9.0.2", + "pg": "^8.13.3", + }, + }, + }, + "packages": { + "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], + + "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="], + + "bcryptjs": ["bcryptjs@2.4.3", "", {}, "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="], + + "body-parser": ["body-parser@1.20.5", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.15.1", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA=="], + + "buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="], + + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="], + + "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + + "debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnsigP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], + + "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], + + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "express": ["express@4.22.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.5", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q=="], + + "finalhandler": ["finalhandler@1.3.2", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "statuses": "~2.0.2", "unpipe": "~1.0.0" } }, "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg=="], + + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], + + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + + "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + + "jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="], + + "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="], + + "jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="], + + "lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="], + + "lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="], + + "lodash.isinteger": ["lodash.isinteger@4.0.4", "", {}, "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="], + + "lodash.isnumber": ["lodash.isnumber@3.0.3", "", {}, "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="], + + "lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="], + + "lodash.isstring": ["lodash.isstring@4.0.1", "", {}, "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="], + + "lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], + + "merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="], + + "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="], + + "mime": ["mime@1.6.0", "", { "bin": "cli.js" }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + + "path-to-regexp": ["path-to-regexp@0.1.13", "", {}, "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA=="], + + "pg": ["pg@8.21.0", "", { "dependencies": { "pg-connection-string": "^2.13.0", "pg-pool": "^3.14.0", "pg-protocol": "^1.14.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.4.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA=="], + + "pg-cloudflare": ["pg-cloudflare@1.4.0", "", {}, "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A=="], + + "pg-connection-string": ["pg-connection-string@2.13.0", "", {}, "sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig=="], + + "pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="], + + "pg-pool": ["pg-pool@3.14.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw=="], + + "pg-protocol": ["pg-protocol@1.14.0", "", {}, "sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA=="], + + "pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="], + + "pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="], + + "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], + + "postgres-bytea": ["postgres-bytea@1.0.1", "", {}, "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ=="], + + "postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="], + + "postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], + + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + + "qs": ["qs@6.15.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@2.5.3", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" } }, "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "semver": ["semver@7.8.3", "", { "bin": "bin/semver.js" }, "sha512-wnilbGyMxzbY7dNOl7jpKbLSjcfeweJWU5j4+u5qW+6/wuGD9KzIGOyZnQVSBM9E7DtWaaH3CyHkppYrKYoxwg=="], + + "send": ["send@0.19.2", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "~0.5.2", "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "~2.4.1", "range-parser": "~1.2.1", "statuses": "~2.0.2" } }, "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg=="], + + "serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "side-channel": ["side-channel@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="], + + "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], + + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + + "type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], + + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + + "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], + + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + + "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], + + "debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "send/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + } +} diff --git a/backend/package-lock.json b/backend/package-lock.json new file mode 100644 index 0000000..417fb10 --- /dev/null +++ b/backend/package-lock.json @@ -0,0 +1,940 @@ +{ + "name": "sig-backend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sig-backend", + "version": "1.0.0", + "dependencies": { + "bcryptjs": "^2.4.3", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.18.2", + "jsonwebtoken": "^9.0.2", + "pg": "^8.13.3" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.5", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "license": "BSD-3-Clause" + }, + "node_modules/bytes": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.22.2", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/jwa": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "license": "MIT" + }, + "node_modules/pg": { + "version": "8.21.0", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.13.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.14.0", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.4.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.4.0", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.13.0", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.14.0", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.14.0", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.2", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.3", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..240b08a --- /dev/null +++ b/backend/package.json @@ -0,0 +1,18 @@ +{ + "name": "sig-backend", + "version": "1.0.0", + "description": "Auth backend for SIG dashboard", + "main": "src/server.js", + "scripts": { + "start": "node src/server.js", + "dev": "node --watch src/server.js" + }, + "dependencies": { + "bcryptjs": "^2.4.3", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.18.2", + "jsonwebtoken": "^9.0.2", + "pg": "^8.13.3" + } +} diff --git a/backend/src/app.js b/backend/src/app.js new file mode 100644 index 0000000..7ded7d7 --- /dev/null +++ b/backend/src/app.js @@ -0,0 +1,27 @@ +require("dotenv").config(); +const express = require("express"); +const cors = require("cors"); +const authRoutes = require("./routes/auth.routes"); +const householdsRoutes = require("./routes/households.routes"); +const poiRoutes = require("./routes/poi.routes"); +const landRoutes = require("./routes/land.routes"); +const gasStationRoutes = require("./routes/gas-station.routes"); + +const app = express(); + +app.use(cors({ + origin: process.env.FRONTEND_URL || "http://localhost:3000", + credentials: true, +})); + +app.use(express.json()); + +app.use("/auth", authRoutes); +app.use("/households", householdsRoutes); +app.use("/poi", poiRoutes); +app.use("/land", landRoutes); +app.use("/gas-station", gasStationRoutes); + +app.get("/health", (_req, res) => res.json({ status: "ok" })); + +module.exports = app; diff --git a/backend/src/db-init.js b/backend/src/db-init.js new file mode 100644 index 0000000..5b63e8c --- /dev/null +++ b/backend/src/db-init.js @@ -0,0 +1,253 @@ +const bcrypt = require("bcryptjs"); +const pool = require("./db"); +const { + SEED_HOUSEHOLDS, + SEED_POI, + SEED_LAND_MARKERS, + SEED_LAND_SHAPES, + SEED_GAS_STATIONS, +} = require("./seed-data"); + +const SALT = 10; + +const SEED_USERS = [ + { username: "superadmin", email: "superadmin@app.local", role: "superadmin", password: "Super@1234" }, + { username: "admin_poverty", email: "poverty@app.local", role: "admin_poverty", password: "Poverty@1234" }, + { username: "admin_lands", email: "lands@app.local", role: "admin_lands_roads", password: "Lands@1234" }, + { username: "admin_gas", email: "gas@app.local", role: "admin_gas_stations",password: "Gas@1234" }, +]; + +async function initDb() { + await pool.query(` + CREATE TABLE IF NOT EXISTS users ( + id SERIAL PRIMARY KEY, + username VARCHAR(255) UNIQUE NOT NULL, + email VARCHAR(255) UNIQUE NOT NULL, + role VARCHAR(100) NOT NULL, + password_hash TEXT NOT NULL + ) + `); + + await pool.query(` + CREATE TABLE IF NOT EXISTS poor_households ( + id SERIAL PRIMARY KEY, + head_name VARCHAR(255) NOT NULL, + family_count INT NOT NULL DEFAULT 1, + poverty_level VARCHAR(10) NOT NULL DEFAULT 'Miskin', + latitude DECIMAL(10, 8) NOT NULL, + longitude DECIMAL(11, 8) NOT NULL, + notes TEXT, + penghasilan INT NOT NULL DEFAULT 0, + kondisi_rumah SMALLINT NOT NULL DEFAULT 5, + kepemilikan_aset SMALLINT NOT NULL DEFAULT 5, + marker_type VARCHAR(50) DEFAULT 'marker', + created_at TIMESTAMPTZ DEFAULT NOW() + ) + `); + + await pool.query(` + CREATE TABLE IF NOT EXISTS poi_markers ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + poi_type VARCHAR(50) NOT NULL, + religion_subtype VARCHAR(50), + latitude DECIMAL(10,8) NOT NULL, + longitude DECIMAL(11,8) NOT NULL, + notes TEXT, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() + ) + `); + + await pool.query(` + ALTER TABLE poor_households + ADD COLUMN IF NOT EXISTS fuzzy_score DECIMAL(5,2) DEFAULT NULL + `); + + await pool.query(` + ALTER TABLE poor_households + ADD COLUMN IF NOT EXISTS fuzzy_label VARCHAR(50) DEFAULT NULL + `); + + await pool.query(` + ALTER TABLE poi_markers + ADD COLUMN IF NOT EXISTS radius_meters INT DEFAULT 0 + `); + + await pool.query(` + CREATE TABLE IF NOT EXISTS land_markers ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + marker_type VARCHAR(50) NOT NULL, + latitude DECIMAL(10,8) NOT NULL, + longitude DECIMAL(11,8) NOT NULL, + notes TEXT, + created_by INT REFERENCES users(id) ON DELETE SET NULL, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() + ) + `); + + await pool.query(` + CREATE INDEX IF NOT EXISTS idx_land_markers_created_by ON land_markers(created_by) + `); + + await pool.query(` + CREATE TABLE IF NOT EXISTS land_shapes ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + shape_type VARCHAR(50) NOT NULL, + status VARCHAR(50), + latitude DECIMAL(10,8) NOT NULL, + longitude DECIMAL(11,8) NOT NULL, + coordinates JSONB, + radius_meters DECIMAL(10,2), + notes TEXT, + created_by INT REFERENCES users(id) ON DELETE SET NULL, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() + ) + `); + + await pool.query(` + CREATE INDEX IF NOT EXISTS idx_land_shapes_created_by ON land_shapes(created_by) + `); + + await pool.query(` + CREATE TABLE IF NOT EXISTS gas_station_markers ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + marker_category VARCHAR(50) NOT NULL, + brand VARCHAR(50), + sub_type VARCHAR(100), + gas_types JSONB, + operating_hours VARCHAR(255), + status VARCHAR(50), + latitude DECIMAL(10,8) NOT NULL, + longitude DECIMAL(11,8) NOT NULL, + notes TEXT, + created_by INT REFERENCES users(id) ON DELETE SET NULL, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() + ) + `); + + await pool.query(` + CREATE INDEX IF NOT EXISTS idx_gas_station_markers_created_by ON gas_station_markers(created_by) + `); + + await pool.query(` + CREATE INDEX IF NOT EXISTS idx_gas_station_markers_category ON gas_station_markers(marker_category) + `); + + await pool.query(` + CREATE TABLE IF NOT EXISTS pertamina_fuel_prices ( + id SERIAL PRIMARY KEY, + last_fetched_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + raw_data JSONB NOT NULL + ) + `); + + // ── Seed Users ────────────────────────────────────────────────────────────── + for (const u of SEED_USERS) { + const hash = await bcrypt.hash(u.password, SALT); + await pool.query( + `INSERT INTO users (username, email, role, password_hash) + VALUES ($1, $2, $3, $4) + ON CONFLICT (username) DO NOTHING`, + [u.username, u.email, u.role, hash] + ); + } + console.log(`[seed] users seeded (${SEED_USERS.length} entries)`); + + // ── Seed Poor Households ──────────────────────────────────────────────────── + const hhCount = await pool.query("SELECT COUNT(*)::int AS c FROM poor_households"); + if (hhCount.rows[0].c === 0) { + for (const h of SEED_HOUSEHOLDS) { + await pool.query( + `INSERT INTO poor_households + (head_name, family_count, poverty_level, latitude, longitude, notes, + penghasilan, kondisi_rumah, kepemilikan_aset, fuzzy_score, fuzzy_label) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)`, + [ + h.head_name, h.family_count, h.poverty_level, + h.latitude, h.longitude, h.notes, + h.penghasilan, h.kondisi_rumah, h.kepemilikan_aset, + h.fuzzy_score, h.fuzzy_label, + ] + ); + } + console.log(`[seed] poor_households seeded (${SEED_HOUSEHOLDS.length} entries)`); + } + + // ── Seed POI Markers ──────────────────────────────────────────────────────── + const poiCount = await pool.query("SELECT COUNT(*)::int AS c FROM poi_markers"); + if (poiCount.rows[0].c === 0) { + for (const p of SEED_POI) { + await pool.query( + `INSERT INTO poi_markers (name, poi_type, religion_subtype, latitude, longitude, notes, radius_meters) + VALUES ($1,$2,$3,$4,$5,$6,$7)`, + [p.name, p.poi_type, p.religion_subtype, p.latitude, p.longitude, p.notes, p.radius_meters] + ); + } + console.log(`[seed] poi_markers seeded (${SEED_POI.length} entries)`); + } + + // ── Seed Land Markers ─────────────────────────────────────────────────────── + const lmCount = await pool.query("SELECT COUNT(*)::int AS c FROM land_markers"); + if (lmCount.rows[0].c === 0) { + // Get superadmin id for created_by + const adminRow = await pool.query("SELECT id FROM users WHERE username = 'superadmin' LIMIT 1"); + const createdBy = adminRow.rows.length ? adminRow.rows[0].id : null; + + for (const lm of SEED_LAND_MARKERS) { + await pool.query( + `INSERT INTO land_markers (name, marker_type, latitude, longitude, notes, created_by) + VALUES ($1,$2,$3,$4,$5,$6)`, + [lm.name, lm.marker_type, lm.latitude, lm.longitude, lm.notes, createdBy] + ); + } + console.log(`[seed] land_markers seeded (${SEED_LAND_MARKERS.length} entries)`); + } + + // ── Seed Land Shapes ──────────────────────────────────────────────────────── + const lsCount = await pool.query("SELECT COUNT(*)::int AS c FROM land_shapes"); + if (lsCount.rows[0].c === 0) { + const adminRow2 = await pool.query("SELECT id FROM users WHERE username = 'superadmin' LIMIT 1"); + const createdBy2 = adminRow2.rows.length ? adminRow2.rows[0].id : null; + + for (const ls of SEED_LAND_SHAPES) { + await pool.query( + `INSERT INTO land_shapes (name, shape_type, status, latitude, longitude, coordinates, radius_meters, notes, created_by) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)`, + [ls.name, ls.shape_type, ls.status, ls.latitude, ls.longitude, ls.coordinates, ls.radius_meters, ls.notes, createdBy2] + ); + } + console.log(`[seed] land_shapes seeded (${SEED_LAND_SHAPES.length} entries)`); + } + + // ── Seed Gas Station Markers ──────────────────────────────────────────────── + const gsCount = await pool.query("SELECT COUNT(*)::int AS c FROM gas_station_markers"); + if (gsCount.rows[0].c === 0) { + const adminRow3 = await pool.query("SELECT id FROM users WHERE username = 'superadmin' LIMIT 1"); + const createdBy3 = adminRow3.rows.length ? adminRow3.rows[0].id : null; + + for (const gs of SEED_GAS_STATIONS) { + await pool.query( + `INSERT INTO gas_station_markers + (name, marker_category, brand, sub_type, gas_types, operating_hours, status, latitude, longitude, notes, created_by) + VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)`, + [ + gs.name, gs.marker_category, gs.brand, gs.sub_type, + gs.gas_types, gs.operating_hours, gs.status, + gs.latitude, gs.longitude, gs.notes, createdBy3, + ] + ); + } + console.log(`[seed] gas_station_markers seeded (${SEED_GAS_STATIONS.length} entries)`); + } + + console.log("[seed] Database initialization complete."); +} + +module.exports = initDb; diff --git a/backend/src/db.js b/backend/src/db.js new file mode 100644 index 0000000..40f3fd2 --- /dev/null +++ b/backend/src/db.js @@ -0,0 +1,7 @@ +const { Pool } = require("pg"); + +const pool = new Pool({ + connectionString: process.env.DATABASE_URL, +}); + +module.exports = pool; diff --git a/backend/src/routes/auth.routes.js b/backend/src/routes/auth.routes.js new file mode 100644 index 0000000..994ca63 --- /dev/null +++ b/backend/src/routes/auth.routes.js @@ -0,0 +1,71 @@ +const express = require("express"); +const bcrypt = require("bcryptjs"); +const jwt = require("jsonwebtoken"); +const { findByUsernameOrEmail } = require("../users"); + +const router = express.Router(); + +const JWT_SECRET = process.env.JWT_SECRET || "change-me-in-production"; +const JWT_EXPIRES_IN = "8h"; + +router.post("/login", async (req, res) => { + const username = req.body.username?.trim(); + const password = req.body.password; + + if (!username || !password) { + return res.status(400).json({ error: "username and password are required" }); + } + + const user = await findByUsernameOrEmail(username); + if (!user) { + return res.status(401).json({ error: "Invalid credentials" }); + } + + const match = await bcrypt.compare(password, user.password_hash); + if (!match) { + return res.status(401).json({ error: "Invalid credentials" }); + } + + const payload = { + sub: user.id, + username: user.username, + email: user.email, + role: user.role, + }; + + const token = jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN }); + + return res.json({ + token, + user: { + id: user.id, + username: user.username, + email: user.email, + role: user.role, + }, + }); +}); + +router.get("/me", (req, res) => { + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith("Bearer ")) { + return res.status(401).json({ error: "Unauthorized" }); + } + + const token = authHeader.slice(7); + try { + const payload = jwt.verify(token, JWT_SECRET); + return res.json({ + user: { + id: payload.sub, + username: payload.username, + email: payload.email, + role: payload.role, + }, + }); + } catch { + return res.status(401).json({ error: "Invalid token" }); + } +}); + +module.exports = router; diff --git a/backend/src/routes/gas-station.routes.js b/backend/src/routes/gas-station.routes.js new file mode 100644 index 0000000..ebef1b3 --- /dev/null +++ b/backend/src/routes/gas-station.routes.js @@ -0,0 +1,339 @@ +const express = require("express"); +const jwt = require("jsonwebtoken"); +const pool = require("../db"); +const fs = require("fs"); +const path = require("path"); + +const router = express.Router(); +const JWT_SECRET = process.env.JWT_SECRET || "change-me-in-production"; + +// Parser helper +function parseFuelData(apiResponse) { + if (!apiResponse || !apiResponse.data || !apiResponse.data.content) { + throw new Error("Invalid API response structure"); + } + + const content = apiResponse.data.content; + const productTableNode = Object.values(content).find( + node => node && node.type && node.type.resolvedName === "ProductTable" + ); + + if (!productTableNode || !productTableNode.props || !Array.isArray(productTableNode.props.items)) { + throw new Error("ProductTable not found in content"); + } + + const items = productTableNode.props.items; + + const headingNode = Object.values(content).find( + node => node && node.type && node.type.resolvedName === "Heading" + ); + const lastUpdated = headingNode?.props?.text || apiResponse.data.title || "Update terbaru"; + + const gasoline = []; + const diesel = []; + + items.forEach(item => { + const isGasoline = item.title && (item.title.toLowerCase() === "gasoline" || item.title.toLowerCase() === "bensin"); + const list = isGasoline ? gasoline : diesel; + + if (Array.isArray(item.data)) { + item.data.forEach(row => { + const parsed = { + region: row.REGION?.trim(), + pertalite: null, + pertamax: null, + pertamaxGreen: null, + pertamaxTurbo: null, + pertamaxPertashop: null, + pertaminaDex: null, + dexlite: null, + bioSolarNonSubsidi: null, + bioSolarSubsidi: null + }; + + for (const [key, value] of Object.entries(row)) { + if (key === "REGION") continue; + + const cleanVal = value?.trim().replace(/,/g, ""); + const numericVal = (cleanVal && cleanVal !== "-" && !isNaN(cleanVal)) ? parseInt(cleanVal, 10) : null; + + if (key.includes("product-table-pertalite.png")) { + parsed.pertalite = numericVal; + } else if (key.includes("product-table-pertamax.png")) { + parsed.pertamax = numericVal; + } else if (key.includes("product-table-pertamax-green-95.png")) { + parsed.pertamaxGreen = numericVal; + } else if (key.includes("product-table-pertamax-turbo.png")) { + parsed.pertamaxTurbo = numericVal; + } else if (key.includes("harga-produk-pertamax-pertashop.jpg")) { + parsed.pertamaxPertashop = numericVal; + } else if (key.includes("product-table-pertamina-dex.png")) { + parsed.pertaminaDex = numericVal; + } else if (key.includes("product-table-dexlite.png")) { + parsed.dexlite = numericVal; + } else if (key.includes("harga-produk-bio-solar-non-subsidi.jpg")) { + parsed.bioSolarNonSubsidi = numericVal; + } else if (key.includes("harga-produk-bio-solar-subsidi.jpg")) { + parsed.bioSolarSubsidi = numericVal; + } + } + + list.push(parsed); + }); + } + }); + + return { + succeeded: true, + lastUpdated, + gasoline, + diesel + }; +} + +// GET latest Pertamina fuel prices (cached, daily fetch) +router.get("/fuel-prices", async (_req, res) => { + try { + // 1. Check if we have a fresh DB cache + const { rows } = await pool.query( + "SELECT * FROM pertamina_fuel_prices ORDER BY last_fetched_at DESC LIMIT 1" + ); + + let rawData = null; + let shouldFetch = true; + + if (rows.length > 0) { + const cache = rows[0]; + const now = new Date(); + const lastFetched = new Date(cache.last_fetched_at); + const diffMs = now - lastFetched; + + // If cached less than 24 hours ago, use cache + if (diffMs < 24 * 60 * 60 * 1000) { + rawData = cache.raw_data; + shouldFetch = false; + console.log("Serving fuel prices from DB cache"); + } + } + + if (shouldFetch) { + console.log("DB Cache stale or missing. Fetching fresh fuel prices from Pertamina API..."); + try { + const response = await fetch( + "https://pertaminapatraniaga.com/api/api/v1/post/get-by-slug/page/harga-terbaru-bbm?language=id" + ); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + rawData = await response.json(); + + // Save to DB cache + await pool.query( + "INSERT INTO pertamina_fuel_prices (last_fetched_at, raw_data) VALUES (NOW(), $1)", + [JSON.stringify(rawData)] + ); + console.log("Successfully cached fresh fuel prices to DB"); + } catch (fetchErr) { + console.error("Failed to fetch fresh fuel prices from Pertamina API:", fetchErr); + // Fall back to stale cache from DB if available + if (rows.length > 0) { + rawData = rows[0].raw_data; + console.warn("Serving stale fuel prices from DB cache after fetch failure"); + } else { + // If no DB cache exists, read from local fallback file + console.warn("No DB cache available. Loading from pertamina-fallback.json file"); + const fallbackPath = path.join(__dirname, "pertamina-fallback.json"); + const fileData = fs.readFileSync(fallbackPath, "utf8"); + rawData = JSON.parse(fileData); + } + } + } + + const parsed = parseFuelData(rawData); + res.json(parsed); + } catch (err) { + console.error("Error in /fuel-prices endpoint:", err); + res.status(500).json({ error: "Internal server error" }); + } +}); + + +function requireAuth(req, res, next) { + const authHeader = req.headers.authorization; + if (!authHeader?.startsWith("Bearer ")) { + return res.status(401).json({ error: "Unauthorized" }); + } + try { + req.user = jwt.verify(authHeader.slice(7), JWT_SECRET); + next(); + } catch { + res.status(401).json({ error: "Invalid token" }); + } +} + +// GET all gas station markers +router.get("/markers", async (_req, res) => { + try { + const { rows } = await pool.query(` + SELECT gsm.*, u.username AS created_by_username + FROM gas_station_markers gsm + LEFT JOIN users u ON u.id = gsm.created_by + ORDER BY gsm.created_at DESC + `); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Database error" }); + } +}); + +// GET stats +router.get("/stats", async (_req, res) => { + try { + const totalResult = await pool.query("SELECT COUNT(*) FROM gas_station_markers"); + const categoryResult = await pool.query(` + SELECT marker_category, COUNT(*) as count + FROM gas_station_markers + GROUP BY marker_category + `); + const activeEvResult = await pool.query(` + SELECT COUNT(*) FROM gas_station_markers + WHERE marker_category = 'charging-station' AND status = 'Active' + `); + + const stats = { + total: parseInt(totalResult.rows[0].count, 10), + gas_pump: 0, + charging_station: 0, + charging_station_active: parseInt(activeEvResult.rows[0].count, 10), + wrench: 0 + }; + + categoryResult.rows.forEach(r => { + const cat = r.marker_category; + if (cat === "gas-pump") stats.gas_pump = parseInt(r.count, 10); + else if (cat === "charging-station") stats.charging_station = parseInt(r.count, 10); + else if (cat === "wrench") stats.wrench = parseInt(r.count, 10); + }); + + res.json(stats); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Database error" }); + } +}); + +// GET single marker +router.get("/markers/:id", async (req, res) => { + try { + const { rows } = await pool.query(` + SELECT gsm.*, u.username AS created_by_username + FROM gas_station_markers gsm + LEFT JOIN users u ON u.id = gsm.created_by + WHERE gsm.id = $1 + `, [req.params.id]); + if (!rows.length) return res.status(404).json({ error: "Not found" }); + res.json(rows[0]); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Database error" }); + } +}); + +// POST new marker +router.post("/markers", requireAuth, async (req, res) => { + const { name, marker_category, brand, sub_type, gas_types, operating_hours, status, latitude, longitude, notes } = req.body; + + if (!name || !marker_category || latitude == null || longitude == null) { + return res.status(400).json({ error: "name, marker_category, latitude, longitude are required" }); + } + + try { + const { rows } = await pool.query( + `INSERT INTO gas_station_markers ( + name, marker_category, brand, sub_type, gas_types, operating_hours, status, latitude, longitude, notes, created_by + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) + RETURNING *`, + [ + name, + marker_category, + brand ?? null, + sub_type ?? null, + gas_types ? JSON.stringify(gas_types) : null, + operating_hours ?? null, + status ?? null, + latitude, + longitude, + notes ?? null, + req.user.sub + ] + ); + res.status(201).json({ ...rows[0], created_by_username: req.user.username }); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Database error" }); + } +}); + +// PUT update marker +router.put("/markers/:id", requireAuth, async (req, res) => { + const { name, marker_category, brand, sub_type, gas_types, operating_hours, status, latitude, longitude, notes } = req.body; + + try { + const { rows } = await pool.query(` + WITH updated AS ( + UPDATE gas_station_markers + SET name = COALESCE($1, name), + marker_category = COALESCE($2, marker_category), + brand = $3, + sub_type = $4, + gas_types = $5, + operating_hours = $6, + status = $7, + latitude = COALESCE($8, latitude), + longitude = COALESCE($9, longitude), + notes = $10, + updated_at = NOW() + WHERE id = $11 + RETURNING * + ) + SELECT updated.*, u.username AS created_by_username + FROM updated + LEFT JOIN users u ON u.id = updated.created_by + `, [ + name ?? null, + marker_category ?? null, + brand ?? null, + sub_type ?? null, + gas_types ? JSON.stringify(gas_types) : null, + operating_hours ?? null, + status ?? null, + latitude ?? null, + longitude ?? null, + notes ?? null, + req.params.id + ]); + if (!rows.length) return res.status(404).json({ error: "Not found" }); + res.json(rows[0]); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Database error" }); + } +}); + +// DELETE marker +router.delete("/markers/:id", requireAuth, async (req, res) => { + try { + const { rowCount } = await pool.query( + "DELETE FROM gas_station_markers WHERE id = $1", + [req.params.id] + ); + if (!rowCount) return res.status(404).json({ error: "Not found" }); + res.status(204).send(); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Database error" }); + } +}); + +module.exports = router; diff --git a/backend/src/routes/households.routes.js b/backend/src/routes/households.routes.js new file mode 100644 index 0000000..1452c57 --- /dev/null +++ b/backend/src/routes/households.routes.js @@ -0,0 +1,311 @@ +const express = require("express"); +const jwt = require("jsonwebtoken"); +const pool = require("../db"); + +const router = express.Router(); +const JWT_SECRET = process.env.JWT_SECRET || "change-me-in-production"; +const FUZZY_URL = process.env.FUZZY_SERVICE_URL ?? "http://localhost:5001"; + +function requireAuth(req, res, next) { + const authHeader = req.headers.authorization; + if (!authHeader?.startsWith("Bearer ")) { + return res.status(401).json({ error: "Unauthorized" }); + } + try { + req.user = jwt.verify(authHeader.slice(7), JWT_SECRET); + next(); + } catch { + res.status(401).json({ error: "Invalid token" }); + } +} + +const FUZZY_LABEL_TO_POVERTY_LEVEL = { + "SANGAT TINGGI": "Extreme", + "TINGGI": "Extreme", + "SEDANG": "Miskin", + "RENDAH": "Miskin", + "TIDAK PRIORITAS": "Rentan", +}; + +async function callFuzzyService(id, penghasilan, tanggungan, kondisi_rumah, kepemilikan_aset) { + try { + const res = await fetch(`${FUZZY_URL}/compute`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ id, penghasilan, tanggungan, kondisi_rumah, kepemilikan_aset }), + }); + if (!res.ok) return null; + return await res.json(); // { id, score, label } + } catch { + return null; // degrade gracefully when service is unavailable + } +} + +router.get("/stats-fuzzy", async (_req, res) => { + try { + const { rows } = await pool.query(` + SELECT fuzzy_label, COUNT(*) AS count + FROM poor_households + WHERE fuzzy_label IS NOT NULL + GROUP BY fuzzy_label + `); + const labels = ["SANGAT TINGGI", "TINGGI", "SEDANG", "RENDAH", "TIDAK PRIORITAS"]; + const stats = Object.fromEntries(labels.map((l) => [l, 0])); + for (const row of rows) stats[row.fuzzy_label] = parseInt(row.count, 10); + res.json(stats); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +// Aggregated, DB-backed numbers for the poverty dashboard. +router.get("/stats-overview", async (_req, res) => { + try { + const [totalsQ, labelsQ, monthlyQ, poiQ] = await Promise.all([ + pool.query(` + SELECT COUNT(*)::int AS household_count, + COALESCE(SUM(family_count),0)::int AS total_dependents, + COALESCE(AVG(penghasilan),0)::float AS avg_income, + AVG(fuzzy_score)::float AS avg_score + FROM poor_households + `), + pool.query(` + SELECT fuzzy_label, COUNT(*)::int AS count + FROM poor_households + WHERE fuzzy_label IS NOT NULL + GROUP BY fuzzy_label + `), + pool.query(` + SELECT to_char(date_trunc('month', created_at), 'YYYY-MM') AS month, + COUNT(*)::int AS count, + AVG(fuzzy_score)::float AS avg_score + FROM poor_households + WHERE created_at >= date_trunc('month', NOW()) - INTERVAL '11 months' + GROUP BY 1 + ORDER BY 1 + `), + pool.query(` + SELECT poi_type, COUNT(*)::int AS count + FROM poi_markers + GROUP BY poi_type + `), + ]); + + const labels = ["SANGAT TINGGI", "TINGGI", "SEDANG", "RENDAH", "TIDAK PRIORITAS"]; + const byLabel = Object.fromEntries(labels.map((l) => [l, 0])); + for (const row of labelsQ.rows) byLabel[row.fuzzy_label] = row.count; + + res.json({ + totals: totalsQ.rows[0], + by_label: byLabel, + monthly: monthlyQ.rows, + poi: Object.fromEntries(poiQ.rows.map((r) => [r.poi_type, r.count])), + }); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +// Re-score every stored household with the current fuzzy engine. +router.post("/recompute-fuzzy", requireAuth, async (_req, res) => { + try { + const { rows } = await pool.query( + `SELECT id, penghasilan, family_count, kondisi_rumah, kepemilikan_aset + FROM poor_households` + ); + if (!rows.length) return res.json({ updated: 0, total: 0 }); + + const fuzzyRes = await fetch(`${FUZZY_URL}/batch`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + households: rows.map((r) => ({ + id: r.id, + penghasilan: r.penghasilan, + tanggungan: r.family_count, + kondisi_rumah: r.kondisi_rumah, + kepemilikan_aset: r.kepemilikan_aset, + })), + }), + }); + if (!fuzzyRes.ok) { + return res.status(502).json({ error: "Fuzzy service unavailable" }); + } + const { results } = await fuzzyRes.json(); + + let updated = 0; + for (const r of results) { + const povertyLevel = FUZZY_LABEL_TO_POVERTY_LEVEL[r.label]; + const { rowCount } = await pool.query( + `UPDATE poor_households + SET fuzzy_score = $1, fuzzy_label = $2, + poverty_level = COALESCE($3, poverty_level) + WHERE id = $4`, + [r.score, r.label, povertyLevel ?? null, r.id] + ); + updated += rowCount; + } + res.json({ updated, total: rows.length }); + } catch { + res.status(500).json({ error: "Recompute failed" }); + } +}); + +router.get("/", async (_req, res) => { + try { + const { rows } = await pool.query( + "SELECT * FROM poor_households ORDER BY created_at DESC" + ); + res.json(rows); + } catch (err) { + res.status(500).json({ error: "Database error" }); + } +}); + +router.post("/", requireAuth, async (req, res) => { + const { + head_name, + family_count, + latitude, + longitude, + notes, + penghasilan, + kondisi_rumah, + kepemilikan_aset, + marker_type, + } = req.body; + + if (!head_name || latitude == null || longitude == null) { + return res.status(400).json({ error: "head_name, latitude, longitude are required" }); + } + + try { + const { rows } = await pool.query( + `INSERT INTO poor_households + (head_name, family_count, poverty_level, latitude, longitude, + notes, penghasilan, kondisi_rumah, kepemilikan_aset, marker_type) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) + RETURNING *`, + [ + head_name, + family_count ?? 1, + "Miskin", + latitude, + longitude, + notes ?? null, + penghasilan ?? 0, + kondisi_rumah ?? 5, + kepemilikan_aset ?? 5, + marker_type ?? "marker", + ] + ); + + const household = rows[0]; + + const fuzzyResult = await callFuzzyService( + household.id, + household.penghasilan, + household.family_count, // family_count = tanggungan + household.kondisi_rumah, + household.kepemilikan_aset + ); + + if (fuzzyResult) { + const povertyLevel = FUZZY_LABEL_TO_POVERTY_LEVEL[fuzzyResult.label] ?? household.poverty_level; + await pool.query( + `UPDATE poor_households SET fuzzy_score = $1, fuzzy_label = $2, poverty_level = $3 WHERE id = $4`, + [fuzzyResult.score, fuzzyResult.label, povertyLevel, household.id] + ); + household.fuzzy_score = fuzzyResult.score; + household.fuzzy_label = fuzzyResult.label; + household.fuzzy_detail = fuzzyResult.faktor ?? null; // not persisted, response only + household.poverty_level = povertyLevel; + } + + res.status(201).json(household); + } catch (err) { + res.status(500).json({ error: "Database error" }); + } +}); + +router.put("/:id", requireAuth, async (req, res) => { + const { + head_name, + family_count, + latitude, + longitude, + notes, + penghasilan, + kondisi_rumah, + kepemilikan_aset, + } = req.body; + + try { + const { rows } = await pool.query( + `UPDATE poor_households + SET head_name = COALESCE($1, head_name), + family_count = COALESCE($2, family_count), + latitude = COALESCE($3, latitude), + longitude = COALESCE($4, longitude), + notes = $5, + penghasilan = COALESCE($6, penghasilan), + kondisi_rumah = COALESCE($7, kondisi_rumah), + kepemilikan_aset= COALESCE($8, kepemilikan_aset) + WHERE id = $9 + RETURNING *`, + [ + head_name ?? null, + family_count ?? null, + latitude ?? null, + longitude ?? null, + notes ?? null, + penghasilan ?? null, + kondisi_rumah ?? null, + kepemilikan_aset ?? null, + req.params.id, + ] + ); + + if (!rows.length) return res.status(404).json({ error: "Not found" }); + + const household = rows[0]; + const fuzzyResult = await callFuzzyService( + household.id, + household.penghasilan, + household.family_count, + household.kondisi_rumah, + household.kepemilikan_aset + ); + + if (fuzzyResult) { + const povertyLevel = FUZZY_LABEL_TO_POVERTY_LEVEL[fuzzyResult.label] ?? household.poverty_level; + await pool.query( + `UPDATE poor_households SET fuzzy_score = $1, fuzzy_label = $2, poverty_level = $3 WHERE id = $4`, + [fuzzyResult.score, fuzzyResult.label, povertyLevel, household.id] + ); + household.fuzzy_score = fuzzyResult.score; + household.fuzzy_label = fuzzyResult.label; + household.fuzzy_detail = fuzzyResult.faktor ?? null; // not persisted, response only + household.poverty_level = povertyLevel; + } + + res.json(household); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +router.delete("/:id", requireAuth, async (req, res) => { + try { + const { rowCount } = await pool.query( + "DELETE FROM poor_households WHERE id = $1", + [req.params.id] + ); + if (!rowCount) return res.status(404).json({ error: "Not found" }); + res.status(204).send(); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +module.exports = router; diff --git a/backend/src/routes/land.routes.js b/backend/src/routes/land.routes.js new file mode 100644 index 0000000..286eacb --- /dev/null +++ b/backend/src/routes/land.routes.js @@ -0,0 +1,233 @@ +const express = require("express"); +const jwt = require("jsonwebtoken"); +const pool = require("../db"); + +const router = express.Router(); +const JWT_SECRET = process.env.JWT_SECRET || "change-me-in-production"; + +function requireAuth(req, res, next) { + const authHeader = req.headers.authorization; + if (!authHeader?.startsWith("Bearer ")) { + return res.status(401).json({ error: "Unauthorized" }); + } + try { + req.user = jwt.verify(authHeader.slice(7), JWT_SECRET); + next(); + } catch { + res.status(401).json({ error: "Invalid token" }); + } +} + +/* ───────── land_markers (marker / flag / protected / registry) ───────── */ + +router.get("/markers", async (_req, res) => { + try { + const { rows } = await pool.query(` + SELECT lm.*, u.username AS created_by_username + FROM land_markers lm + LEFT JOIN users u ON u.id = lm.created_by + ORDER BY lm.created_at DESC + `); + res.json(rows); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +router.get("/markers/:id", async (req, res) => { + try { + const { rows } = await pool.query(` + SELECT lm.*, u.username AS created_by_username + FROM land_markers lm + LEFT JOIN users u ON u.id = lm.created_by + WHERE lm.id = $1 + `, [req.params.id]); + if (!rows.length) return res.status(404).json({ error: "Not found" }); + res.json(rows[0]); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +router.post("/markers", requireAuth, async (req, res) => { + const { name, marker_type, latitude, longitude, notes } = req.body; + + if (!name || !marker_type || latitude == null || longitude == null) { + return res.status(400).json({ error: "name, marker_type, latitude, longitude are required" }); + } + + try { + const { rows } = await pool.query( + `INSERT INTO land_markers (name, marker_type, latitude, longitude, notes, created_by) + VALUES ($1, $2, $3, $4, $5, $6) + RETURNING *`, + [name, marker_type, latitude, longitude, notes ?? null, req.user.sub] + ); + res.status(201).json({ ...rows[0], created_by_username: req.user.username }); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +router.put("/markers/:id", requireAuth, async (req, res) => { + const { name, marker_type, latitude, longitude, notes } = req.body; + + try { + const { rows } = await pool.query(` + WITH updated AS ( + UPDATE land_markers + SET name = COALESCE($1, name), + marker_type = COALESCE($2, marker_type), + latitude = COALESCE($3, latitude), + longitude = COALESCE($4, longitude), + notes = $5, + updated_at = NOW() + WHERE id = $6 + RETURNING * + ) + SELECT updated.*, u.username AS created_by_username + FROM updated + LEFT JOIN users u ON u.id = updated.created_by + `, [ + name ?? null, + marker_type ?? null, + latitude ?? null, + longitude ?? null, + notes ?? null, + req.params.id + ]); + if (!rows.length) return res.status(404).json({ error: "Not found" }); + res.json(rows[0]); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +router.delete("/markers/:id", requireAuth, async (req, res) => { + try { + const { rowCount } = await pool.query( + "DELETE FROM land_markers WHERE id = $1", + [req.params.id] + ); + if (!rowCount) return res.status(404).json({ error: "Not found" }); + res.status(204).send(); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +/* ───────── land_shapes (line / polygon / circle) ───────── */ + +router.get("/shapes", async (_req, res) => { + try { + const { rows } = await pool.query(` + SELECT ls.*, u.username AS created_by_username + FROM land_shapes ls + LEFT JOIN users u ON u.id = ls.created_by + ORDER BY ls.created_at DESC + `); + res.json(rows); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +router.get("/shapes/:id", async (req, res) => { + try { + const { rows } = await pool.query(` + SELECT ls.*, u.username AS created_by_username + FROM land_shapes ls + LEFT JOIN users u ON u.id = ls.created_by + WHERE ls.id = $1 + `, [req.params.id]); + if (!rows.length) return res.status(404).json({ error: "Not found" }); + res.json(rows[0]); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +router.post("/shapes", requireAuth, async (req, res) => { + const { name, shape_type, status, latitude, longitude, coordinates, radius_meters, notes } = req.body; + + if (!name || !shape_type || latitude == null || longitude == null) { + return res.status(400).json({ error: "name, shape_type, latitude, longitude are required" }); + } + + try { + const { rows } = await pool.query( + `INSERT INTO land_shapes (name, shape_type, status, latitude, longitude, coordinates, radius_meters, notes, created_by) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) + RETURNING *`, + [ + name, + shape_type, + status ?? null, + latitude, + longitude, + coordinates ? JSON.stringify(coordinates) : null, + radius_meters ?? null, + notes ?? null, + req.user.sub + ] + ); + res.status(201).json({ ...rows[0], created_by_username: req.user.username }); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +router.put("/shapes/:id", requireAuth, async (req, res) => { + const { name, shape_type, status, latitude, longitude, coordinates, radius_meters, notes } = req.body; + + try { + const { rows } = await pool.query(` + WITH updated AS ( + UPDATE land_shapes + SET name = COALESCE($1, name), + shape_type = COALESCE($2, shape_type), + status = $3, + latitude = COALESCE($4, latitude), + longitude = COALESCE($5, longitude), + coordinates = COALESCE($6, coordinates), + radius_meters = COALESCE($7, radius_meters), + notes = $8, + updated_at = NOW() + WHERE id = $9 + RETURNING * + ) + SELECT updated.*, u.username AS created_by_username + FROM updated + LEFT JOIN users u ON u.id = updated.created_by + `, [ + name ?? null, + shape_type ?? null, + status ?? null, + latitude ?? null, + longitude ?? null, + coordinates ? JSON.stringify(coordinates) : null, + radius_meters ?? null, + notes ?? null, + req.params.id + ]); + if (!rows.length) return res.status(404).json({ error: "Not found" }); + res.json(rows[0]); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +router.delete("/shapes/:id", requireAuth, async (req, res) => { + try { + const { rowCount } = await pool.query( + "DELETE FROM land_shapes WHERE id = $1", + [req.params.id] + ); + if (!rowCount) return res.status(404).json({ error: "Not found" }); + res.status(204).send(); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +module.exports = router; diff --git a/backend/src/routes/pertamina-fallback.json b/backend/src/routes/pertamina-fallback.json new file mode 100644 index 0000000..dba82b2 --- /dev/null +++ b/backend/src/routes/pertamina-fallback.json @@ -0,0 +1 @@ +{"succeeded":true,"message":null,"errors":null,"errorCode":null,"data":{"title":"Update as of May 4, 2026","content":{"ROOT":{"type":{"resolvedName":"Main"},"isCanvas":true,"props":{},"displayName":"Main","custom":{},"hidden":false,"nodes":["hg8Iyys7EE","DT48C-vFLU"],"linkedNodes":{}},"hg8Iyys7EE":{"type":{"resolvedName":"Grid"},"isCanvas":true,"props":{"padding":20,"column":"1","backgroundColor":"white","mode":"default","gap":"0"},"displayName":"Grid","custom":{},"parent":"ROOT","hidden":false,"nodes":["I4g0NozOW4","wz6WV3adEe","74YCbALP43","l9RNzkhMqY"],"linkedNodes":{}},"I4g0NozOW4":{"type":{"resolvedName":"Heading"},"isCanvas":false,"props":{"sizeWidth":"1/1","align":"left","text":"Update as of June 10, 2026","fontWeight":"bold","headingType":"h3","variant":"default","backgroundColor":"transparent","color":"black","nopaddingY":true,"nopaddingX":true},"displayName":"Heading","custom":{},"parent":"hg8Iyys7EE","hidden":false,"nodes":[],"linkedNodes":{}},"DT48C-vFLU":{"type":{"resolvedName":"Section"},"isCanvas":true,"props":{"padding":20},"displayName":"Section","custom":{},"parent":"ROOT","hidden":true,"nodes":["LaMSV0A7SD"],"linkedNodes":{}},"LaMSV0A7SD":{"type":{"resolvedName":"CTANavigation"},"isCanvas":true,"props":{"navigationMenu":[{"id":1,"title":"Berita","url":"/news/","image":"2024/06/news-banner.jpeg","isLocal":false},{"id":2,"title":"Karir","url":"/page/karir/","image":"2024/06/karir.jpeg","isLocal":false}],"align":"left"},"displayName":"CTA Navigation","custom":{},"parent":"DT48C-vFLU","hidden":false,"nodes":["Redl-b09JL"],"linkedNodes":{}},"Redl-b09JL":{"type":{"resolvedName":"TabsPostCarousel"},"isCanvas":true,"props":{"padding":20},"displayName":"TabsPostCarousel","custom":{},"parent":"LaMSV0A7SD","hidden":false,"nodes":[],"linkedNodes":{}},"l9RNzkhMqY":{"type":{"resolvedName":"ProductTable"},"isCanvas":false,"props":{"items":[{"data":[{"REGION":"Prov. Aceh","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Free Trade Zone (FTZ) Sabang","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"15,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"15,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sumatera Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sumatera Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,650","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"17,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Riau","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,650","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"17,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Kepulauan Riau","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,650","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"17,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Free Trade Zone (FTZ) Batam","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"19,700","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"15,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"15,400","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Jambi","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Bengkulu","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sumatera Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Bangka-Belitung","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Lampung","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. DKI Jakarta","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":"17,000","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Banten","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":"17,000","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Jawa Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":"17,000","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Jawa Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":"17,000","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. DI Yogyakarta","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":"17,000","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Jawa Timur","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":"17,000","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Bali","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Nusa Tenggara Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Nusa Tenggara Timur","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Kalimantan Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Kalimantan Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Kalimantan Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,650","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"17,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Kalimantan Timur","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Kalimantan Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,650","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"17,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sulawesi Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Gorontalo","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sulawesi Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sulawesi Tenggara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sulawesi Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sulawesi Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Maluku","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Maluku Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Papua","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Papua Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Papua Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Papua Pegunungan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Papua Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Papua Barat Daya","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"}],"title":"Gasoline"},{"data":[{"REGION":"Prov. Aceh","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Free Trade Zone (FTZ) Sabang","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"21,550","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sumatera Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sumatera Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"24,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Riau","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"24,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Kepulauan Riau","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"24,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Free Trade Zone (FTZ) Batam","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"23,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"21,850","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Jambi","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Bengkulu","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sumatera Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Bangka-Belitung","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Lampung","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. DKI Jakarta","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Banten","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Jawa Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Jawa Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. DI Yogyakarta","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Jawa Timur","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Bali","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Nusa Tenggara Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Nusa Tenggara Timur","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":"22,900","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Kalimantan Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Kalimantan Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Kalimantan Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"24,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Kalimantan Timur","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Kalimantan Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"24,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sulawesi Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Gorontalo","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sulawesi Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sulawesi Tenggara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sulawesi Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sulawesi Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Maluku","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Maluku Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Papua","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Papua Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Papua Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Papua Pegunungan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Papua Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Papua Barat Daya","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"}],"title":"Gasoil"}]},"displayName":"Product Table","custom":{},"parent":"hg8Iyys7EE","hidden":false,"nodes":[],"linkedNodes":{}},"wz6WV3adEe":{"type":{"resolvedName":"Spacer"},"isCanvas":false,"props":{"padding":20},"displayName":"Spacer","custom":{},"parent":"hg8Iyys7EE","hidden":false,"nodes":[],"linkedNodes":{}},"74YCbALP43":{"type":{"resolvedName":"Spacer"},"isCanvas":false,"props":{"padding":20},"displayName":"Spacer","custom":{},"parent":"hg8Iyys7EE","hidden":false,"nodes":[],"linkedNodes":{}}},"excerpt":null,"type":1,"status":0,"parentId":null,"url":null,"_Status":"Draft","_Type":"Page","slug":"harga-terbaru-bbm","postTypeSlug":"page/harga-terbaru-bbm","postTypeTitle":"page - Harga Terbaru","featuredImage":"2024/07/harga-terbaru-bbm.png","thumbnailImage":"2024/07/harga-terbaru-bbm.png","postHierarchy":null,"parent":null,"subNavs":[],"categories":[],"tags":[],"meta":{"enableTransparentHeader":true,"pageTitleStyle":"default","pageTitle":"Latest Fuel Price","pageTitleImage":"2024/07/harga-terbaru-bbm.png"},"order":0,"typeDisplay":"Halaman","i81n":[{"postId":"2792833d-f0c8-450d-1cd9-08dea8d3a332","title":"Update as of May 4, 2026","excerpt":null,"content":{"ROOT":{"type":{"resolvedName":"Main"},"isCanvas":true,"props":{},"displayName":"Main","custom":{},"hidden":false,"nodes":["hg8Iyys7EE","DT48C-vFLU"],"linkedNodes":{}},"hg8Iyys7EE":{"type":{"resolvedName":"Grid"},"isCanvas":true,"props":{"padding":20,"column":"1","backgroundColor":"white","mode":"default","gap":"0"},"displayName":"Grid","custom":{},"parent":"ROOT","hidden":false,"nodes":["I4g0NozOW4","wz6WV3adEe","74YCbALP43","l9RNzkhMqY"],"linkedNodes":{}},"I4g0NozOW4":{"type":{"resolvedName":"Heading"},"isCanvas":false,"props":{"sizeWidth":"1/1","align":"left","text":"Update as of June 10, 2026","fontWeight":"bold","headingType":"h3","variant":"default","backgroundColor":"transparent","color":"black","nopaddingY":true,"nopaddingX":true},"displayName":"Heading","custom":{},"parent":"hg8Iyys7EE","hidden":false,"nodes":[],"linkedNodes":{}},"DT48C-vFLU":{"type":{"resolvedName":"Section"},"isCanvas":true,"props":{"padding":20},"displayName":"Section","custom":{},"parent":"ROOT","hidden":true,"nodes":["LaMSV0A7SD"],"linkedNodes":{}},"LaMSV0A7SD":{"type":{"resolvedName":"CTANavigation"},"isCanvas":true,"props":{"navigationMenu":[{"id":1,"title":"Berita","url":"/news/","image":"2024/06/news-banner.jpeg","isLocal":false},{"id":2,"title":"Karir","url":"/page/karir/","image":"2024/06/karir.jpeg","isLocal":false}],"align":"left"},"displayName":"CTA Navigation","custom":{},"parent":"DT48C-vFLU","hidden":false,"nodes":["Redl-b09JL"],"linkedNodes":{}},"Redl-b09JL":{"type":{"resolvedName":"TabsPostCarousel"},"isCanvas":true,"props":{"padding":20},"displayName":"TabsPostCarousel","custom":{},"parent":"LaMSV0A7SD","hidden":false,"nodes":[],"linkedNodes":{}},"l9RNzkhMqY":{"type":{"resolvedName":"ProductTable"},"isCanvas":false,"props":{"items":[{"data":[{"REGION":"Prov. Aceh","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Free Trade Zone (FTZ) Sabang","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"15,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"15,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sumatera Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sumatera Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,650","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"17,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Riau","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,650","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"17,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Kepulauan Riau","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,650","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"17,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Free Trade Zone (FTZ) Batam","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"19,700","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"15,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"15,400","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Jambi","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Bengkulu","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sumatera Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Bangka-Belitung","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Lampung","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. DKI Jakarta","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":"17,000","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Banten","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":"17,000","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Jawa Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":"17,000","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Jawa Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":"17,000","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. DI Yogyakarta","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":"17,000","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Jawa Timur","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":"17,000","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Bali","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Nusa Tenggara Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Nusa Tenggara Timur","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"20,750","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,250","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,150","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Kalimantan Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Kalimantan Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Kalimantan Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,650","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"17,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Kalimantan Timur","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Kalimantan Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,650","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"17,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sulawesi Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Gorontalo","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sulawesi Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sulawesi Tenggara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sulawesi Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Sulawesi Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Maluku","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Maluku Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Papua","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":"21,200","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Papua Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Papua Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Papua Pegunungan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Papua Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"},{"REGION":"Prov. Papua Barat Daya","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-turbo.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax-green-95.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamax.png":"16,650","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-pertamax-pertashop.jpg":"16,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertalite.png":"10,000"}],"title":"Gasoline"},{"data":[{"REGION":"Prov. Aceh","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Free Trade Zone (FTZ) Sabang","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"21,550","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sumatera Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sumatera Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"24,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Riau","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"24,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Kepulauan Riau","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"24,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Free Trade Zone (FTZ) Batam","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"23,550","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"21,850","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Jambi","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Bengkulu","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sumatera Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Bangka-Belitung","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Lampung","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. DKI Jakarta","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Banten","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Jawa Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Jawa Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. DI Yogyakarta","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Jawa Timur","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Bali","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Nusa Tenggara Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Nusa Tenggara Timur","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"24,800","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":"22,900","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Kalimantan Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Kalimantan Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Kalimantan Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"24,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Kalimantan Timur","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Kalimantan Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,900","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"24,000","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sulawesi Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Gorontalo","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sulawesi Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sulawesi Tenggara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sulawesi Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Sulawesi Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Maluku","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Maluku Utara","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Papua","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Papua Barat","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Papua Selatan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Papua Pegunungan","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Papua Tengah","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":" - ","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"},{"REGION":"Prov. Papua Barat Daya","https://pertaminapatraniaga.com/file/files/2024/08/product-table-pertamina-dex.png":"25,350","https://pertaminapatraniaga.com/file/files/2024/08/product-table-dexlite.png":"23,500","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-non-subsidi.jpg":" - ","https://pertaminapatraniaga.com/file/files/2026/05/harga-produk-bio-solar-subsidi.jpg":"6,800"}],"title":"Gasoil"}]},"displayName":"Product Table","custom":{},"parent":"hg8Iyys7EE","hidden":false,"nodes":[],"linkedNodes":{}},"wz6WV3adEe":{"type":{"resolvedName":"Spacer"},"isCanvas":false,"props":{"padding":20},"displayName":"Spacer","custom":{},"parent":"hg8Iyys7EE","hidden":false,"nodes":[],"linkedNodes":{}},"74YCbALP43":{"type":{"resolvedName":"Spacer"},"isCanvas":false,"props":{"padding":20},"displayName":"Spacer","custom":{},"parent":"hg8Iyys7EE","hidden":false,"nodes":[],"linkedNodes":{}}},"language":"id"}],"postReaction":null,"startDate":"2026-06-09T16:21:39.212206+00:00","endDate":null,"pinned":false,"featured":false,"reacted":false,"createdDate":"2026-05-03T17:01:29.2126173+00:00","createdBy":"f05b5ef8-ea96-4c98-b0b4-affe9d838c17","modifiedDate":"2026-06-09T16:21:39.2712045+00:00","modifiedBy":"f05b5ef8-ea96-4c98-b0b4-affe9d838c17","id":"2792833d-f0c8-450d-1cd9-08dea8d3a332"},"statusCode":200} \ No newline at end of file diff --git a/backend/src/routes/poi.routes.js b/backend/src/routes/poi.routes.js new file mode 100644 index 0000000..3c32ad1 --- /dev/null +++ b/backend/src/routes/poi.routes.js @@ -0,0 +1,112 @@ +const express = require("express"); +const jwt = require("jsonwebtoken"); +const pool = require("../db"); + +const router = express.Router(); +const JWT_SECRET = process.env.JWT_SECRET || "change-me-in-production"; + +function requireAuth(req, res, next) { + const authHeader = req.headers.authorization; + if (!authHeader?.startsWith("Bearer ")) { + return res.status(401).json({ error: "Unauthorized" }); + } + try { + req.user = jwt.verify(authHeader.slice(7), JWT_SECRET); + next(); + } catch { + res.status(401).json({ error: "Invalid token" }); + } +} + +router.get("/", async (_req, res) => { + try { + const { rows } = await pool.query( + "SELECT * FROM poi_markers ORDER BY created_at DESC" + ); + res.json(rows); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +router.get("/:id", async (req, res) => { + try { + const { rows } = await pool.query( + "SELECT * FROM poi_markers WHERE id = $1", + [req.params.id] + ); + if (!rows.length) return res.status(404).json({ error: "Not found" }); + res.json(rows[0]); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +router.post("/", requireAuth, async (req, res) => { + const { name, poi_type, religion_subtype, latitude, longitude, notes, radius_meters } = req.body; + + if (!name || !poi_type || latitude == null || longitude == null) { + return res.status(400).json({ error: "name, poi_type, latitude, longitude are required" }); + } + + try { + const { rows } = await pool.query( + `INSERT INTO poi_markers (name, poi_type, religion_subtype, latitude, longitude, notes, radius_meters) + VALUES ($1, $2, $3, $4, $5, $6, $7) + RETURNING *`, + [name, poi_type, religion_subtype ?? null, latitude, longitude, notes ?? null, radius_meters ?? 0] + ); + res.status(201).json(rows[0]); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +router.put("/:id", requireAuth, async (req, res) => { + const { name, poi_type, religion_subtype, latitude, longitude, notes, radius_meters } = req.body; + + try { + const { rows } = await pool.query( + `UPDATE poi_markers + SET name = COALESCE($1, name), + poi_type = COALESCE($2, poi_type), + religion_subtype = $3, + latitude = COALESCE($4, latitude), + longitude = COALESCE($5, longitude), + notes = $6, + radius_meters = COALESCE($7, radius_meters), + updated_at = NOW() + WHERE id = $8 + RETURNING *`, + [ + name ?? null, + poi_type ?? null, + religion_subtype ?? null, + latitude ?? null, + longitude ?? null, + notes ?? null, + radius_meters ?? null, + req.params.id + ] + ); + if (!rows.length) return res.status(404).json({ error: "Not found" }); + res.json(rows[0]); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +router.delete("/:id", requireAuth, async (req, res) => { + try { + const { rowCount } = await pool.query( + "DELETE FROM poi_markers WHERE id = $1", + [req.params.id] + ); + if (!rowCount) return res.status(404).json({ error: "Not found" }); + res.status(204).send(); + } catch { + res.status(500).json({ error: "Database error" }); + } +}); + +module.exports = router; diff --git a/backend/src/seed-data.js b/backend/src/seed-data.js new file mode 100644 index 0000000..b5e1d81 --- /dev/null +++ b/backend/src/seed-data.js @@ -0,0 +1,358 @@ +/** + * Seed data for all dashboard tables. + * All coordinates are in the Pontianak / Kalimantan Barat area. + */ + +// ─── Poverty Households (poor_households) ──────────────────────────────────── +const SEED_HOUSEHOLDS = [ + { + head_name: "Budi Santoso", + family_count: 5, + poverty_level: "Extreme", + latitude: -0.0263, + longitude: 109.3425, + notes: "Rumah semi-permanen, atap bocor", + penghasilan: 800000, + kondisi_rumah: 2, + kepemilikan_aset: 1, + fuzzy_score: 92.5, + fuzzy_label: "SANGAT TINGGI", + }, + { + head_name: "Siti Aminah", + family_count: 4, + poverty_level: "Extreme", + latitude: -0.0301, + longitude: 109.3388, + notes: "Janda, 3 anak sekolah", + penghasilan: 600000, + kondisi_rumah: 2, + kepemilikan_aset: 2, + fuzzy_score: 88.3, + fuzzy_label: "SANGAT TINGGI", + }, + { + head_name: "Ahmad Hidayat", + family_count: 6, + poverty_level: "Miskin", + latitude: -0.0195, + longitude: 109.3501, + notes: "Buruh harian lepas", + penghasilan: 1200000, + kondisi_rumah: 3, + kepemilikan_aset: 3, + fuzzy_score: 72.1, + fuzzy_label: "TINGGI", + }, + { + head_name: "Dewi Lestari", + family_count: 3, + poverty_level: "Miskin", + latitude: -0.0352, + longitude: 109.3290, + notes: "Pedagang kecil di pasar", + penghasilan: 1500000, + kondisi_rumah: 4, + kepemilikan_aset: 3, + fuzzy_score: 65.0, + fuzzy_label: "TINGGI", + }, + { + head_name: "Rahmat Kurniawan", + family_count: 7, + poverty_level: "Extreme", + latitude: -0.0410, + longitude: 109.3460, + notes: "Tinggal di bantaran sungai", + penghasilan: 500000, + kondisi_rumah: 1, + kepemilikan_aset: 1, + fuzzy_score: 95.8, + fuzzy_label: "SANGAT TINGGI", + }, + { + head_name: "Nurul Hasanah", + family_count: 2, + poverty_level: "Miskin", + latitude: -0.0180, + longitude: 109.3550, + notes: "Ibu rumah tangga, suami sakit", + penghasilan: 900000, + kondisi_rumah: 3, + kepemilikan_aset: 2, + fuzzy_score: 78.4, + fuzzy_label: "TINGGI", + }, + { + head_name: "Hasan Basri", + family_count: 4, + poverty_level: "Rentan", + latitude: -0.0225, + longitude: 109.3610, + notes: "Tukang ojek online", + penghasilan: 2000000, + kondisi_rumah: 5, + kepemilikan_aset: 4, + fuzzy_score: 45.2, + fuzzy_label: "SEDANG", + }, + { + head_name: "Fatimah Zahra", + family_count: 3, + poverty_level: "Rentan", + latitude: -0.0290, + longitude: 109.3320, + notes: "Penjahit rumahan", + penghasilan: 1800000, + kondisi_rumah: 5, + kepemilikan_aset: 5, + fuzzy_score: 38.6, + fuzzy_label: "RENDAH", + }, + { + head_name: "Eko Prasetyo", + family_count: 5, + poverty_level: "Miskin", + latitude: -0.0150, + longitude: 109.3480, + notes: "Nelayan kecil, pendapatan tidak tetap", + penghasilan: 1100000, + kondisi_rumah: 3, + kepemilikan_aset: 2, + fuzzy_score: 70.9, + fuzzy_label: "TINGGI", + }, + { + head_name: "Yanti Suryani", + family_count: 2, + poverty_level: "Rentan", + latitude: -0.0330, + longitude: 109.3570, + notes: "Pekerja warung makan", + penghasilan: 2200000, + kondisi_rumah: 6, + kepemilikan_aset: 5, + fuzzy_score: 28.4, + fuzzy_label: "RENDAH", + }, + { + head_name: "Muhammad Rizki", + family_count: 8, + poverty_level: "Extreme", + latitude: -0.0380, + longitude: 109.3400, + notes: "Pengangguran, 7 tanggungan", + penghasilan: 400000, + kondisi_rumah: 1, + kepemilikan_aset: 1, + fuzzy_score: 97.2, + fuzzy_label: "SANGAT TINGGI", + }, + { + head_name: "Sri Wahyuni", + family_count: 3, + poverty_level: "Rentan", + latitude: -0.0205, + longitude: 109.3650, + notes: "Guru honorer", + penghasilan: 2500000, + kondisi_rumah: 6, + kepemilikan_aset: 6, + fuzzy_score: 15.3, + fuzzy_label: "TIDAK PRIORITAS", + }, +]; + +// ─── POI Markers (poi_markers) ─────────────────────────────────────────────── +const SEED_POI = [ + { name: "Masjid Jami' Pontianak", poi_type: "religion", religion_subtype: "mosque", latitude: -0.0220, longitude: 109.3420, notes: "Masjid terbesar di pusat kota", radius_meters: 500 }, + { name: "Gereja Katedral Pontianak", poi_type: "religion", religion_subtype: "church", latitude: -0.0245, longitude: 109.3380, notes: "Gereja bersejarah", radius_meters: 300 }, + { name: "Vihara Bodhisattva Karaniya", poi_type: "religion", religion_subtype: "temple", latitude: -0.0198, longitude: 109.3455, notes: "Vihara terbesar", radius_meters: 200 }, + { name: "Puskesmas Pontianak Selatan", poi_type: "clinic", religion_subtype: null, latitude: -0.0310, longitude: 109.3350, notes: "Puskesmas utama kecamatan selatan", radius_meters: 1000 }, + { name: "Klinik Pratama Sehat Sentosa", poi_type: "clinic", religion_subtype: null, latitude: -0.0270, longitude: 109.3500, notes: "Klinik 24 jam", radius_meters: 800 }, + { name: "Lumbung Pangan Desa Siantan", poi_type: "food-bank",religion_subtype: null, latitude: -0.0160, longitude: 109.3440, notes: "Distribusi beras untuk warga miskin",radius_meters: 1500 }, + { name: "SDN 01 Pontianak Barat", poi_type: "school", religion_subtype: null, latitude: -0.0235, longitude: 109.3310, notes: "Sekolah dasar negeri", radius_meters: 600 }, + { name: "SMP Negeri 3 Pontianak", poi_type: "school", religion_subtype: null, latitude: -0.0180, longitude: 109.3520, notes: "SMP favorit", radius_meters: 700 }, +]; + +// ─── Land Markers (land_markers) ───────────────────────────────────────────── +const SEED_LAND_MARKERS = [ + { name: "Tugu Khatulistiwa", marker_type: "marker", latitude: 0.0000, longitude: 109.3228, notes: "Monumen landmark utama" }, + { name: "Kantor Gubernur Kalbar", marker_type: "flag", latitude: -0.0234, longitude: 109.3412, notes: "Pusat pemerintahan provinsi" }, + { name: "Hutan Lindung Gunung Palung", marker_type: "protected", latitude: -1.2100, longitude: 110.1500, notes: "Kawasan hutan lindung nasional" }, + { name: "BPN Pontianak", marker_type: "registry", latitude: -0.0280, longitude: 109.3360, notes: "Kantor pertanahan kota" }, + { name: "Area Mangrove Kakap", marker_type: "protected", latitude: -0.1050, longitude: 109.1200, notes: "Zona konservasi mangrove" }, + { name: "Terminal Ambawang", marker_type: "flag", latitude: -0.0800, longitude: 109.4100, notes: "Terminal bus utama" }, +]; + +// ─── Land Shapes (land_shapes) ─────────────────────────────────────────────── +const SEED_LAND_SHAPES = [ + { + name: "Zona Permukiman Siantan", + shape_type: "polygon", + status: "Hak Milik", + latitude: -0.0150, + longitude: 109.3440, + coordinates: JSON.stringify([ + [-0.0130, 109.3420], [-0.0130, 109.3460], + [-0.0170, 109.3460], [-0.0170, 109.3420], + ]), + radius_meters: null, + notes: "Kawasan permukiman padat penduduk", + }, + { + name: "Lahan Pertanian Rasau Jaya", + shape_type: "polygon", + status: "Hak Guna Usaha", + latitude: -0.1500, + longitude: 109.3800, + coordinates: JSON.stringify([ + [-0.1450, 109.3750], [-0.1450, 109.3850], + [-0.1550, 109.3850], [-0.1550, 109.3750], + ]), + radius_meters: null, + notes: "Lahan pertanian padi, luas ~100 ha", + }, + { + name: "Area Industri Sei Raya", + shape_type: "circle", + status: "Hak Guna Bangunan", + latitude: -0.0500, + longitude: 109.3600, + coordinates: null, + radius_meters: 800, + notes: "Kawasan industri ringan", + }, + { + name: "Jalan Tanjungpura", + shape_type: "line", + status: "Tanah Negara", + latitude: -0.0250, + longitude: 109.3400, + coordinates: JSON.stringify([ + [-0.0200, 109.3350], [-0.0300, 109.3450], + ]), + radius_meters: null, + notes: "Jalan arteri utama kota, panjang ~3 km", + }, +]; + +// ─── Gas Station Markers (gas_station_markers) ─────────────────────────────── +const SEED_GAS_STATIONS = [ + { + name: "SPBU 64.751.01 Pontianak", + marker_category: "gas-pump", + brand: "Pertamina", + sub_type: null, + gas_types: JSON.stringify(["Pertalite", "Pertamax", "Pertamina Dex", "Bio Solar"]), + operating_hours: "24 Jam", + status: null, + latitude: -0.0230, + longitude: 109.3390, + notes: "SPBU utama di pusat kota", + }, + { + name: "SPBU 64.751.03 Ahmad Yani", + marker_category: "gas-pump", + brand: "Pertamina", + sub_type: null, + gas_types: JSON.stringify(["Pertalite", "Pertamax", "Bio Solar"]), + operating_hours: "06:00 - 22:00", + status: null, + latitude: -0.0310, + longitude: 109.3450, + notes: "Dekat jalan Ahmad Yani", + }, + { + name: "SPBU 64.751.08 Siantan", + marker_category: "gas-pump", + brand: "Pertamina", + sub_type: null, + gas_types: JSON.stringify(["Pertalite", "Bio Solar"]), + operating_hours: "06:00 - 21:00", + status: null, + latitude: -0.0155, + longitude: 109.3520, + notes: "SPBU kawasan Siantan", + }, + { + name: "Shell Gajah Mada", + marker_category: "gas-pump", + brand: "Shell", + sub_type: null, + gas_types: JSON.stringify(["Shell Super", "Shell V-Power", "Shell Diesel"]), + operating_hours: "24 Jam", + status: null, + latitude: -0.0280, + longitude: 109.3310, + notes: "Shell pertama di Pontianak", + }, + { + name: "SPBU 64.751.12 Kubu Raya", + marker_category: "gas-pump", + brand: "Pertamina", + sub_type: null, + gas_types: JSON.stringify(["Pertalite", "Pertamax", "Bio Solar"]), + operating_hours: "06:00 - 22:00", + status: null, + latitude: -0.0620, + longitude: 109.3700, + notes: "SPBU di kabupaten Kubu Raya", + }, + { + name: "EV Charging PLN Pontianak", + marker_category: "charging-station", + brand: null, + sub_type: "Type 2 AC", + gas_types: null, + operating_hours: "24 Jam", + status: "Active", + latitude: -0.0240, + longitude: 109.3430, + notes: "Stasiun pengisian kendaraan listrik PLN", + }, + { + name: "EV Charging Mall Ayani Mega", + marker_category: "charging-station", + brand: null, + sub_type: "CCS2 DC", + gas_types: null, + operating_hours: "10:00 - 22:00", + status: "Active", + latitude: -0.0335, + longitude: 109.3470, + notes: "Fast charging di basement mall", + }, + { + name: "Bengkel Pontianak Motor", + marker_category: "wrench", + brand: null, + sub_type: null, + gas_types: null, + operating_hours: "08:00 - 17:00", + status: null, + latitude: -0.0260, + longitude: 109.3480, + notes: "Bengkel umum motor dan mobil", + }, + { + name: "Bengkel Jaya Abadi", + marker_category: "wrench", + brand: null, + sub_type: null, + gas_types: null, + operating_hours: "08:00 - 18:00", + status: null, + latitude: -0.0190, + longitude: 109.3560, + notes: "Bengkel spesialis AC dan kelistrikan", + }, +]; + +module.exports = { + SEED_HOUSEHOLDS, + SEED_POI, + SEED_LAND_MARKERS, + SEED_LAND_SHAPES, + SEED_GAS_STATIONS, +}; diff --git a/backend/src/server.js b/backend/src/server.js new file mode 100644 index 0000000..c5a1503 --- /dev/null +++ b/backend/src/server.js @@ -0,0 +1,16 @@ +require("dotenv").config(); +const app = require("./app"); +const initDb = require("./db-init"); + +const PORT = process.env.PORT || 4000; + +initDb() + .then(() => { + app.listen(PORT, () => { + console.log(`Auth server running on port ${PORT}`); + }); + }) + .catch((err) => { + console.error("Failed to initialize database:", err); + process.exit(1); + }); diff --git a/backend/src/users.js b/backend/src/users.js new file mode 100644 index 0000000..f25ae8e --- /dev/null +++ b/backend/src/users.js @@ -0,0 +1,11 @@ +const pool = require("./db"); + +async function findByUsernameOrEmail(identifier) { + const { rows } = await pool.query( + "SELECT * FROM users WHERE username = $1 OR email = $1 LIMIT 1", + [identifier] + ); + return rows[0] ?? null; +} + +module.exports = { findByUsernameOrEmail }; diff --git a/docker-compose.coolify.yml b/docker-compose.coolify.yml new file mode 100644 index 0000000..930637f --- /dev/null +++ b/docker-compose.coolify.yml @@ -0,0 +1,69 @@ +# Compose file for Coolify deployments. +# In Coolify: create a "Docker Compose" resource pointing at this repo and set +# "Docker Compose Location" to /docker-compose.coolify.yml. +# +# No host ports are published here on purpose: Coolify's proxy routes the +# auto-assigned domains to the container ports declared via the +# SERVICE_FQDN__ magic variables, so ports never clash with +# other apps on the server. postgres and fuzzy-service stay private on the +# internal network. SERVICE_PASSWORD_* / SERVICE_BASE64_* values are +# generated by Coolify and editable in its UI. +# +# docker-compose.yml (with fixed host ports) remains for local development. + +services: + postgres: + image: postgres:16-alpine + environment: + POSTGRES_DB: ${POSTGRES_DB:-sig} + POSTGRES_USER: ${POSTGRES_USER:-sig} + POSTGRES_PASSWORD: ${SERVICE_PASSWORD_POSTGRES} + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-sig} -d ${POSTGRES_DB:-sig}"] + interval: 5s + timeout: 5s + retries: 10 + restart: unless-stopped + + fuzzy-service: + build: + context: ./fuzzy-service + dockerfile: Dockerfile + restart: unless-stopped + + backend: + build: + context: ./backend + dockerfile: Dockerfile + environment: + - SERVICE_FQDN_BACKEND_4000 + - PORT=4000 + - JWT_SECRET=${SERVICE_BASE64_64_JWT} + - FRONTEND_URL=${SERVICE_URL_FRONTEND} + - DATABASE_URL=postgres://${POSTGRES_USER:-sig}:${SERVICE_PASSWORD_POSTGRES}@postgres:5432/${POSTGRES_DB:-sig} + - FUZZY_SERVICE_URL=http://fuzzy-service:5001 + depends_on: + postgres: + condition: service_healthy + restart: unless-stopped + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile.frontend + args: + # Baked into the browser bundle at build time; must be the backend's + # public URL, which Coolify provides via the magic variable below. + NEXT_PUBLIC_BACKEND_URL: ${SERVICE_URL_BACKEND} + # Server-side rewrites go over the internal network. + BACKEND_URL: http://backend:4000 + environment: + - SERVICE_FQDN_FRONTEND_3000 + depends_on: + - backend + restart: unless-stopped + +volumes: + postgres_data: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..db7218d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,53 @@ +services: + postgres: + image: postgres:16-alpine + environment: + POSTGRES_DB: sig + POSTGRES_USER: sig + POSTGRES_PASSWORD: sig_secret + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + restart: unless-stopped + + fuzzy-service: + build: + context: ./fuzzy-service + dockerfile: Dockerfile + ports: + - "5001:5001" + restart: unless-stopped + + backend: + build: + context: ./backend + dockerfile: Dockerfile + ports: + - "4000:4000" + environment: + PORT: 4000 + JWT_SECRET: replace-with-secure-secret-in-production + FRONTEND_URL: http://localhost:3000 + DATABASE_URL: postgres://sig:sig_secret@postgres:5432/sig + FUZZY_SERVICE_URL: http://fuzzy-service:5001 + depends_on: + - postgres + restart: unless-stopped + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile.frontend + args: + NEXT_PUBLIC_BACKEND_URL: http://localhost:4000 + ports: + - "3000:3000" + environment: + NODE_ENV: production + depends_on: + - backend + restart: unless-stopped + +volumes: + postgres_data: diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..5ef6a52 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md new file mode 100644 index 0000000..8bd0e39 --- /dev/null +++ b/frontend/AGENTS.md @@ -0,0 +1,5 @@ + +# This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. + diff --git a/frontend/CLAUDE.md b/frontend/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/frontend/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/frontend/Dockerfile.frontend b/frontend/Dockerfile.frontend new file mode 100644 index 0000000..5316e62 --- /dev/null +++ b/frontend/Dockerfile.frontend @@ -0,0 +1,26 @@ +FROM oven/bun:1-alpine AS deps +WORKDIR /app +COPY package.json bun.lock ./ +RUN bun install + +FROM oven/bun:1-alpine AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . +ARG NEXT_PUBLIC_BACKEND_URL=http://localhost:4000 +ENV NEXT_PUBLIC_BACKEND_URL=$NEXT_PUBLIC_BACKEND_URL +# Rewrites in next.config.ts are resolved at build time +ARG BACKEND_URL=http://localhost:4000 +ENV BACKEND_URL=$BACKEND_URL +RUN bun run build + +FROM node:20-alpine AS runner +WORKDIR /app +ENV NODE_ENV=production +ENV HOSTNAME=0.0.0.0 +ENV PORT=3000 +COPY --from=builder /app/.next/standalone ./ +COPY --from=builder /app/.next/static ./.next/static +COPY --from=builder /app/public ./public +EXPOSE 3000 +CMD ["node", "server.js"] diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..e215bc4 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/frontend/app/auth/login/page.tsx b/frontend/app/auth/login/page.tsx new file mode 100644 index 0000000..9cf5926 --- /dev/null +++ b/frontend/app/auth/login/page.tsx @@ -0,0 +1,5 @@ +import { AuthPage } from "@/components/auth-page"; + +export default function LoginPage() { + return ; +} diff --git a/frontend/app/dashboard/gas-station/page.tsx b/frontend/app/dashboard/gas-station/page.tsx new file mode 100644 index 0000000..d75a488 --- /dev/null +++ b/frontend/app/dashboard/gas-station/page.tsx @@ -0,0 +1,322 @@ +"use client"; +/* eslint-disable @typescript-eslint/no-explicit-any, react-hooks/set-state-in-effect */ + + +import { useState, useMemo, useEffect } from "react"; +import dynamic from "next/dynamic"; +import { AppShell } from "@/components/app-shell"; +import { MapPageHeader } from "@/components/maps/map-page-header"; +import { GasStationStatsCards } from "@/components/gas-station/gas-station-stats-cards"; +import { GasStationDirectory } from "@/components/gas-station/gas-station-directory"; +import { GasStationInspector } from "@/components/gas-station/gas-station-inspector"; +import { PertaminaPriceTable } from "@/components/gas-station/pertamina-price-table"; +import { GasStationHud } from "@/components/gas-station/gas-station-hud"; +import { GasStationDisclosureCard } from "@/components/ui/collection-grid-disclosure"; +import type { UserMarker } from "@/components/leaflet-map"; +import { FuelIcon } from "@/components/ui/phosphor-icons"; +import { StationPlacementDialog } from "@/components/gas-station/station-placement-dialog"; +import { StationEditDialog } from "@/components/gas-station/station-edit-dialog"; +import { toast } from "sonner"; + +const PLACING_LABELS: Record = { + "charging-station": "Pengisi Daya EV", + "gas-pump": "SPBU", + wrench: "Bengkel", +}; + +const LeafletMap = dynamic(() => import("@/components/leaflet-map"), { ssr: false }); +const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL ?? "http://localhost:4000"; + +export default function GasStationMapPage() { + const [selectedStation, setSelectedStation] = useState(null); + const [searchQuery, setSearchQuery] = useState(""); + const [brandFilter, setBrandFilter] = useState("All"); + const [fuelPriceDisplay, setFuelPriceDisplay] = useState<"ron92" | "ron95" | "diesel">("ron95"); + const [evOnlyFilter, setEvOnlyFilter] = useState(false); + const [useLeaflet, setUseLeaflet] = useState(false); + const [mapStyle, setMapStyle] = useState<"street" | "satellite">("street"); + const [userMarkers, setUserMarkers] = useState([]); + const [placingMarkerType, setPlacingMarkerType] = useState(null); + const [dragDropEnabled, setDragDropEnabled] = useState(false); + + // Custom Marker Dialog State + const [pendingClick, setPendingClick] = useState<{ lat: number; lng: number } | null>(null); + const [pendingMarkerType, setPendingMarkerType] = useState(null); + const [editingMarker, setEditingMarker] = useState(null); + + const [hydrated, setHydrated] = useState(false); + const [refreshKey, setRefreshKey] = useState(0); + const [fuelPrices, setFuelPrices] = useState(null); + const [fuelPricesLoading, setFuelPricesLoading] = useState(true); + + // Fetch dynamic Pertamina fuel prices + useEffect(() => { + setFuelPricesLoading(true); + fetch(`${BACKEND_URL}/gas-station/fuel-prices`) + .then((res) => res.json()) + .then((data) => { + if (data && data.succeeded) { + setFuelPrices(data); + } + }) + .catch((err) => console.error("Gagal memuat harga BBM:", err)) + .finally(() => setFuelPricesLoading(false)); + }, []); + + const filteredStations = useMemo(() => { + return userMarkers.filter((st) => { + const matchesSearch = st.name.toLowerCase().includes(searchQuery.toLowerCase()); + const matchesBrand = brandFilter === "All" || (st.type === "gas-pump" && st.meta?.poi_type === brandFilter); + const matchesEv = !evOnlyFilter || st.type === "charging-station"; + return matchesSearch && matchesBrand && matchesEv; + }); + }, [userMarkers, searchQuery, brandFilter, evOnlyFilter]); + + const activeSelectedStation = useMemo(() => { + if (filteredStations.length === 0) return null; + const found = filteredStations.find((st) => st.id === selectedStation?.id); + if (found) return found; + return filteredStations[0]; + }, [filteredStations, selectedStation]); + + // Load existing markers when page loads or refreshKey changes + useEffect(() => { + const token = localStorage.getItem("auth_token"); + const headers: Record = token ? { Authorization: `Bearer ${token}` } : {}; + + fetch(`${BACKEND_URL}/gas-station/markers`, { headers }) + .then((r) => r.json()) + .then((rows) => { + if (Array.isArray(rows)) { + const mapped = rows.map((row: any) => { + let finalPoiType = ""; + let finalNotes = ""; + + if (row.marker_category === "charging-station") { + finalPoiType = row.sub_type; + finalNotes = row.status ? `Status: ${row.status}` : ""; + } else if (row.marker_category === "gas-pump") { + finalPoiType = row.brand; + finalNotes = row.operating_hours ? `Jam: ${row.operating_hours}` : ""; + } else if (row.marker_category === "wrench") { + finalPoiType = row.sub_type; + finalNotes = row.status; + } + + return { + id: `gsm-${row.id}`, + lat: parseFloat(row.latitude), + lng: parseFloat(row.longitude), + type: row.marker_category, + name: row.name, + meta: { + poi_type: finalPoiType, + notes: finalNotes, + gas_types: row.gas_types || undefined, + }, + }; + }); + setUserMarkers(mapped); + } + }) + .catch((err) => { + console.error("Gagal memuat markers:", err); + }); + }, [refreshKey]); + + // Session storage synchronization (identical pattern to poverty) + useEffect(() => { + setUseLeaflet(sessionStorage.getItem("gas_useLeaflet") === "true"); + setMapStyle((sessionStorage.getItem("gas_mapStyle") as "street" | "satellite") ?? "street"); + setBrandFilter(sessionStorage.getItem("gas_brandFilter") ?? "All"); + setFuelPriceDisplay((sessionStorage.getItem("gas_fuelPriceDisplay") as "ron92" | "ron95" | "diesel") ?? "ron95"); + setHydrated(true); + }, []); + + useEffect(() => { if (hydrated) sessionStorage.setItem("gas_useLeaflet", String(useLeaflet)); }, [useLeaflet, hydrated]); + useEffect(() => { if (hydrated) sessionStorage.setItem("gas_mapStyle", mapStyle); }, [mapStyle, hydrated]); + useEffect(() => { if (hydrated) sessionStorage.setItem("gas_brandFilter", brandFilter); }, [brandFilter, hydrated]); + useEffect(() => { if (hydrated) sessionStorage.setItem("gas_fuelPriceDisplay", fuelPriceDisplay); }, [fuelPriceDisplay, hydrated]); + + // FULL BLEED MAP LAYOUT + if (useLeaflet) { + return ( + setUseLeaflet(false)}> +
+ {/* Leaflet Map */} + { + const found = userMarkers.find((st) => st.id === id); + if (found) setSelectedStation(found); + }} + fuelPriceDisplay={fuelPriceDisplay} + mapStyle={mapStyle} + userMarkers={userMarkers} + onMarkerClick={(m) => { + if (["charging-station", "gas-pump", "wrench"].includes(m.type)) { + setEditingMarker(m); + } + }} + onMapClick={(lat, lng) => { + if (placingMarkerType) { + setPendingClick({ lat, lng }); + setPendingMarkerType(placingMarkerType); + setPlacingMarkerType(null); + } + }} + dragDropEnabled={dragDropEnabled} + onMarkerDragEnd={handleMarkerDragEnd} + /> + + {/* Placing Marker Banner */} + {placingMarkerType && ( +
+ + + Menempatkan {PLACING_LABELS[placingMarkerType] ?? placingMarkerType.replace("-", " ")}... Klik pada peta untuk menentukan posisi + + +
+ )} + + {/* Floating Filters HUD */} + setUseLeaflet(false)} + /> + + {/* Floating Collection Grid Disclosure */} +
+ +
+ + {/* Placement Dialog */} + { if (!v) { setPendingClick(null); setPendingMarkerType(null); } }} + pendingClick={pendingClick} + markerType={pendingMarkerType} + onConfirm={(newMarker) => { + setUserMarkers((prev) => [...prev, newMarker]); + setPendingClick(null); + setPendingMarkerType(null); + setRefreshKey((k) => k + 1); + }} + /> + + {/* Edit Dialog */} + { if (!v) setEditingMarker(null); }} + marker={editingMarker} + onUpdated={(updated) => { + setUserMarkers((prev) => prev.map((m) => m.id === updated.id ? updated : m)); + setEditingMarker(null); + setRefreshKey((k) => k + 1); + }} + onDeleted={(deleted) => { + setUserMarkers((prev) => prev.filter((m) => m.id !== deleted.id)); + setEditingMarker(null); + setRefreshKey((k) => k + 1); + }} + /> +
+
+ ); + } + + async function handleMarkerDragEnd(marker: UserMarker, newLat: number, newLng: number) { + const dbId = marker.id.split("-").slice(1).join("-"); + const token = localStorage.getItem("auth_token"); + const headers = { + "Content-Type": "application/json", + ...(token ? { Authorization: `Bearer ${token}` } : {}), + }; + + try { + const res = await fetch(`${BACKEND_URL}/gas-station/markers/${dbId}`, { + method: "PUT", + headers, + body: JSON.stringify({ latitude: newLat, longitude: newLng }), + }); + + if (!res.ok) throw new Error("Gagal memperbarui posisi di server"); + + setUserMarkers((prev) => + prev.map((m) => + m.id === marker.id ? { ...m, lat: newLat, lng: newLng } : m + ) + ); + toast.success(`Posisi "${marker.name}" berhasil diperbarui`); + setRefreshKey((k) => k + 1); + } catch (err) { + console.error(err); + toast.error(err instanceof Error ? err.message : "Gagal memindahkan penanda"); + } + } + + // STANDARD DASHBOARD LAYOUT + return ( + +
+ {/* Top Header Card */} + setUseLeaflet(true)} + buttonText="Peta Stasiun" + icon={} + /> + + {/* Unified Grid Layout */} +
+ {/* Row 1: 4 stats cards (rendered inside GasStationStatsCards as direct children) */} + + + {/* Row 2: Directory */} +
+ +
+ + {/* Row 2: Inspector (right column) */} +
+ +
+ {/* Row 3: Pricing Table (full width) */} +
+ +
+
+ +
+
+ ); +} diff --git a/frontend/app/dashboard/land/page.tsx b/frontend/app/dashboard/land/page.tsx new file mode 100644 index 0000000..93ba49d --- /dev/null +++ b/frontend/app/dashboard/land/page.tsx @@ -0,0 +1,400 @@ +"use client"; + +import { useState, useMemo, useEffect } from "react"; +import dynamic from "next/dynamic"; +import { AppShell } from "@/components/app-shell"; +import { MapPageHeader } from "@/components/maps/map-page-header"; +import { LandStatsCards } from "@/components/land/land-stats-cards"; +import { LandAllocationChart } from "@/components/land/land-allocation-chart"; +import { LandDirectory } from "@/components/land/land-directory"; +import { LandInspectorTop, LandInspectorBottom } from "@/components/land/land-inspector"; +import { LAND_MARKER_LABELS } from "@/lib/map-data"; +import { LayersIcon } from "@/components/ui/phosphor-icons"; +import { LandDisclosureCard } from "@/components/ui/collection-grid-disclosure"; +import { + ShapePlacementDialog, + LAND_STATUSES, + calcLineLength, + calcPolygonArea, +} from "@/components/land/shape-placement-dialog"; +import { ShapeEditDialog } from "@/components/land/shape-edit-dialog"; +import type { UserMarker } from "@/components/leaflet-map"; +import { toast } from "sonner"; + +const LeafletMap = dynamic(() => import("@/components/leaflet-map"), { ssr: false }); + +const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL ?? "http://localhost:4000"; + +function authHeaders(json = false) { + const token = localStorage.getItem("auth_token"); + return { ...(json ? { "Content-Type": "application/json" } : {}), ...(token ? { Authorization: `Bearer ${token}` } : {}) }; +} + +export default function LandMapPage() { + const [searchQuery, setSearchQuery] = useState(""); + const [visibilityFilters, setVisibilityFilters] = useState>({ + marker: true, + flag: true, + protected: true, + registry: true, + line: true, + polygon: true, + circle: true, + }); + + const [useLeaflet, setUseLeaflet] = useState(false); + const [mapStyle, setMapStyle] = useState<"street" | "satellite">("street"); + const [hydrated, setHydrated] = useState(false); + const [userMarkers, setUserMarkers] = useState([]); + const [refreshKey, setRefreshKey] = useState(0); + const [placingMarkerType, setPlacingMarkerType] = useState(null); + const [dragDropEnabled, setDragDropEnabled] = useState(false); + const [selectedItemId, setSelectedItemId] = useState(null); + + // Shape/marker dialog state + const [pendingShape, setPendingShape] = useState(null); + const [editingShape, setEditingShape] = useState(null); + + // Hydrate persisted view state from sessionStorage on mount + useEffect(() => { + setUseLeaflet(sessionStorage.getItem("land_useLeaflet") === "true"); + setMapStyle((sessionStorage.getItem("land_mapStyle") as "street" | "satellite") ?? "street"); + const storedFilters = sessionStorage.getItem("land_visibilityFilters"); + if (storedFilters) { + try { + setVisibilityFilters(JSON.parse(storedFilters)); + } catch { + /* keep defaults */ + } + } + setSelectedItemId(sessionStorage.getItem("land_selectedItemId")); + setHydrated(true); + }, []); + + useEffect(() => { if (hydrated) sessionStorage.setItem("land_useLeaflet", String(useLeaflet)); }, [useLeaflet, hydrated]); + useEffect(() => { if (hydrated) sessionStorage.setItem("land_mapStyle", mapStyle); }, [mapStyle, hydrated]); + useEffect(() => { if (hydrated) sessionStorage.setItem("land_visibilityFilters", JSON.stringify(visibilityFilters)); }, [visibilityFilters, hydrated]); + useEffect(() => { + if (!hydrated) return; + if (selectedItemId) sessionStorage.setItem("land_selectedItemId", selectedItemId); + else sessionStorage.removeItem("land_selectedItemId"); + }, [selectedItemId, hydrated]); + + // Always-on data fetch — feeds both the dashboard widgets and the map. + useEffect(() => { + const token = localStorage.getItem("auth_token"); + const headers: Record = token ? { Authorization: `Bearer ${token}` } : {}; + + Promise.allSettled([ + fetch(`${BACKEND_URL}/land/markers`, { headers }).then((r) => r.json()), + fetch(`${BACKEND_URL}/land/shapes`, { headers }).then((r) => r.json()), + ]).then(([markersResult, shapesResult]) => { + const markerRows = markersResult.status === "fulfilled" && Array.isArray(markersResult.value) ? markersResult.value : []; + const shapeRows = shapesResult.status === "fulfilled" && Array.isArray(shapesResult.value) ? shapesResult.value : []; + + const pointMarkers: UserMarker[] = markerRows.map((r: any) => ({ + id: `lm-${r.id}`, + lat: parseFloat(r.latitude), + lng: parseFloat(r.longitude), + type: r.marker_type, + name: r.name, + meta: { + notes: r.notes ?? undefined, + created_by_username: r.created_by_username ?? undefined, + created_at: r.created_at ?? undefined, + updated_at: r.updated_at ?? undefined, + }, + })); + + const shapeMarkers: UserMarker[] = shapeRows.map((r: any) => ({ + id: `ls-${r.id}`, + lat: parseFloat(r.latitude), + lng: parseFloat(r.longitude), + type: r.shape_type, + name: r.name, + meta: { + poi_type: r.status ?? undefined, + coordinates: r.coordinates ?? undefined, + radius: r.radius_meters != null ? parseFloat(r.radius_meters) : undefined, + notes: r.notes ?? undefined, + created_by_username: r.created_by_username ?? undefined, + created_at: r.created_at ?? undefined, + updated_at: r.updated_at ?? undefined, + }, + })); + + setUserMarkers([...pointMarkers, ...shapeMarkers]); + }); + }, [refreshKey]); + + const landMarkerItems = useMemo(() => userMarkers.filter((m) => m.id.startsWith("lm-")), [userMarkers]); + const landShapeItems = useMemo(() => userMarkers.filter((m) => m.id.startsWith("ls-")), [userMarkers]); + + const filteredItems = useMemo(() => { + const q = searchQuery.toLowerCase(); + return userMarkers.filter((m) => { + const label = LAND_MARKER_LABELS[m.type] ?? m.type; + return m.name.toLowerCase().includes(q) || label.toLowerCase().includes(q); + }); + }, [searchQuery, userMarkers]); + + const aggregateStats = useMemo(() => { + let totalAreaHa = 0; + let totalRoadKm = 0; + landShapeItems.forEach((m) => { + if (m.type === "polygon" && m.meta?.coordinates) totalAreaHa += calcPolygonArea(m.meta.coordinates) / 10000; + else if (m.type === "line" && m.meta?.coordinates) totalRoadKm += calcLineLength(m.meta.coordinates) / 1000; + }); + return { + totalParcelArea: totalAreaHa.toLocaleString(undefined, { maximumFractionDigits: 1 }), + totalRoadLength: totalRoadKm.toLocaleString(undefined, { maximumFractionDigits: 1 }), + activeMarkers: landMarkerItems.length, + }; + }, [landShapeItems, landMarkerItems]); + + const pieChartData = useMemo(() => { + const totals: Record = {}; + landShapeItems.forEach((m) => { + if (m.type === "polygon" && m.meta?.coordinates) { + const status = m.meta.poi_type ?? "Unknown"; + totals[status] = (totals[status] ?? 0) + calcPolygonArea(m.meta.coordinates) / 10000; + } + }); + return Object.entries(totals).map(([status, value]) => { + const found = LAND_STATUSES.find((s) => s.value === status); + return { name: found?.label ?? status, value: parseFloat(value.toFixed(1)), color: found?.color ?? "#6b7280" }; + }); + }, [landShapeItems]); + + const selectedItem = useMemo(() => userMarkers.find((m) => m.id === selectedItemId) ?? null, [selectedItemId, userMarkers]); + + const recentFlags = useMemo( + () => landMarkerItems + .filter((m) => m.type === "flag") + .sort((a, b) => (b.meta?.created_at ?? "").localeCompare(a.meta?.created_at ?? "")) + .slice(0, 2), + [landMarkerItems] + ); + + const toggleFilter = (type: string) => { + setVisibilityFilters((prev) => ({ + ...prev, + [type]: !prev[type], + })); + }; + + async function handleConfirmPlacement(item: UserMarker) { + const isPointType = !["line", "polygon", "circle"].includes(item.type); + try { + const res = await fetch(`${BACKEND_URL}/land/${isPointType ? "markers" : "shapes"}`, { + method: "POST", + headers: authHeaders(true), + body: JSON.stringify( + isPointType + ? { name: item.name, marker_type: item.type, latitude: item.lat, longitude: item.lng, notes: item.meta?.notes ?? null } + : { + name: item.name, + shape_type: item.type, + status: item.meta?.poi_type ?? null, + latitude: item.lat, + longitude: item.lng, + coordinates: item.meta?.coordinates ?? null, + radius_meters: item.meta?.radius ?? null, + notes: item.meta?.notes ?? null, + } + ), + }); + if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error ?? "Gagal menyimpan"); + setPendingShape(null); + setRefreshKey((k) => k + 1); + toast.success(`"${item.name}" berhasil disimpan`); + } catch (err) { + toast.error(err instanceof Error ? err.message : "Gagal menyimpan"); + } + } + + async function handleUpdateItem(updated: UserMarker) { + const isMarker = updated.id.startsWith("lm-"); + const dbId = updated.id.split("-").slice(1).join("-"); + const url = `${BACKEND_URL}/land/${isMarker ? "markers" : "shapes"}/${dbId}`; + const body = isMarker + ? { name: updated.name, marker_type: updated.type, notes: updated.meta?.notes ?? null } + : { name: updated.name, shape_type: updated.type, status: updated.meta?.poi_type ?? null, notes: updated.meta?.notes ?? null }; + try { + const res = await fetch(url, { method: "PUT", headers: authHeaders(true), body: JSON.stringify(body) }); + if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error ?? "Gagal memperbarui"); + setEditingShape(null); + setRefreshKey((k) => k + 1); + toast.success(`"${updated.name}" berhasil diperbarui`); + } catch (err) { + toast.error(err instanceof Error ? err.message : "Gagal memperbarui"); + } + } + + async function handleDeleteItem(deleted: UserMarker) { + const isMarker = deleted.id.startsWith("lm-"); + const dbId = deleted.id.split("-").slice(1).join("-"); + try { + const res = await fetch(`${BACKEND_URL}/land/${isMarker ? "markers" : "shapes"}/${dbId}`, { method: "DELETE", headers: authHeaders() }); + if (!res.ok) throw new Error("Gagal menghapus"); + setEditingShape(null); + if (selectedItemId === deleted.id) setSelectedItemId(null); + setRefreshKey((k) => k + 1); + toast.success(`"${deleted.name}" berhasil dihapus`); + } catch (err) { + toast.error(err instanceof Error ? err.message : "Gagal menghapus"); + } + } + + async function handleMarkerDragEnd(marker: UserMarker, newLat: number, newLng: number) { + const isMarker = marker.id.startsWith("lm-"); + const dbId = marker.id.split("-").slice(1).join("-"); + try { + const res = await fetch(`${BACKEND_URL}/land/${isMarker ? "markers" : "shapes"}/${dbId}`, { + method: "PUT", + headers: authHeaders(true), + body: JSON.stringify({ latitude: newLat, longitude: newLng }), + }); + if (!res.ok) throw new Error("Gagal memindahkan penanda"); + setUserMarkers((prev) => prev.map((m) => (m.id === marker.id ? { ...m, lat: newLat, lng: newLng } : m))); + toast.success(`Posisi "${marker.name}" berhasil diperbarui`); + } catch (err) { + toast.error(err instanceof Error ? err.message : "Gagal memindahkan penanda"); + } + } + + // FULL BLEED MAP LAYOUT + if (useLeaflet) { + return ( + setUseLeaflet(false)}> +
+ {/* Leaflet Map */} + { + // Open placement dialog instead of adding directly + setPendingShape(shape); + setPlacingMarkerType(null); + }} + onMarkerClick={(marker) => { + setEditingShape(marker); + setSelectedItemId(marker.id); + }} + onMapClick={(lat, lng) => { + if (placingMarkerType && !["line", "polygon", "circle"].includes(placingMarkerType)) { + setPendingShape({ id: `pending-${Date.now()}`, lat, lng, type: placingMarkerType, name: "" }); + setPlacingMarkerType(null); + } + }} + dragDropEnabled={dragDropEnabled} + onMarkerDragEnd={handleMarkerDragEnd} + /> + + {/* Placing Marker Banner */} + {placingMarkerType && ( +
+ + + Menempatkan {LAND_MARKER_LABELS[placingMarkerType] ?? placingMarkerType.replace("-", " ")}... Klik pada peta untuk menentukan posisi + + +
+ )} + + {/* Floating Collection Grid Disclosure */} +
+ +
+ + {/* Shape Placement Dialog */} + { if (!v) setPendingShape(null); }} + pendingShape={pendingShape} + onConfirm={handleConfirmPlacement} + /> + + {/* Shape Edit Dialog */} + { if (!v) setEditingShape(null); }} + shape={editingShape} + onUpdated={handleUpdateItem} + onDeleted={handleDeleteItem} + /> +
+
+ ); + } + + // STANDARD DASHBOARD LAYOUT + return ( + +
+ {/* Top Header Card */} + setUseLeaflet(true)} + buttonText="Peta Lahan" + icon={} + /> + + {/* Unified Grid Layout */} +
+ {/* Row 1: 3 stats cards (rendered inside LandStatsCards as direct children) */} + + + {/* Row 2: Allocation Chart */} +
+ +
+ + {/* Row 2: Inspector Top */} +
+ +
+ + {/* Row 3: Inspector Bottom */} +
+ +
+ + {/* Row 3: Directory */} +
+ setSelectedItemId(item.id)} + /> +
+
+ +
+
+ ); +} diff --git a/frontend/app/dashboard/page.tsx b/frontend/app/dashboard/page.tsx new file mode 100644 index 0000000..092c5dc --- /dev/null +++ b/frontend/app/dashboard/page.tsx @@ -0,0 +1,27 @@ +"use client"; + +import { AppShell } from "@/components/app-shell"; +import { Dashboard } from "@/components/dashboard"; +import { HouseIcon } from "@/components/ui/phosphor-icons"; + +export default function DashboardPage() { + return ( + +
+ {/* Page Header */} +
+
+

+ Beranda Analitik +

+
+

+ Overview status statistik, data kemiskinan, peta wilayah, dan operasional SPBU. +

+
+ + +
+
+ ); +} diff --git a/frontend/app/dashboard/poverty/page.tsx b/frontend/app/dashboard/poverty/page.tsx new file mode 100644 index 0000000..59899a9 --- /dev/null +++ b/frontend/app/dashboard/poverty/page.tsx @@ -0,0 +1,389 @@ +"use client"; + +import { useState, useMemo, useEffect, useRef } from "react"; +import dynamic from "next/dynamic"; +import { DndContext, DragOverlay, useDroppable, useSensor, useSensors, PointerSensor, type DragEndEvent } from "@dnd-kit/core"; +import { AppShell } from "@/components/app-shell"; +import { MapPageHeader } from "@/components/maps/map-page-header"; +import { PovertyStatsCards } from "@/components/poverty/poverty-stats-cards"; +import { PovertyTrendChart } from "@/components/poverty/poverty-trend-chart"; +import { PovertyDirectory } from "@/components/poverty/poverty-directory"; +import { PovertyInspectorTop, PovertyInspectorBottom } from "@/components/poverty/poverty-inspector"; +import { PovertyHud } from "@/components/poverty/poverty-hud"; +import { TrendingDownIcon } from "@/components/ui/phosphor-icons"; +import { PovertyDisclosureCard } from "@/components/ui/collection-grid-disclosure"; +import type { UserMarker } from "@/components/leaflet-map"; +import type { HouseholdRow, PovertyOverview } from "@/lib/poverty-types"; +import { MarkerPlacementDialog } from "@/components/poverty/marker-placement-dialog"; +import { ReligionDialog } from "@/components/poverty/religion-dialog"; +import { MarkerEditDialog } from "@/components/poverty/marker-edit-dialog"; +import { PovertyHouseholdsTable } from "@/components/poverty/poverty-households-table"; +import { FuzzyPriorityStats } from "@/components/poverty/fuzzy-priority-stats"; +import { toast } from "sonner"; + +const LeafletMap = dynamic(() => import("@/components/leaflet-map"), { ssr: false }); + +const PLACING_LABELS: Record = { + marker: "Data Warga", + religion: "Rumah Ibadah", + clinic: "Klinik", + "food-bank": "Lumbung Pangan", + school: "Sekolah", +}; + +function MapDropZone() { + const { setNodeRef } = useDroppable({ id: "map-canvas" }); + return
; +} + +export default function PovertyMapPage() { + const [searchQuery, setSearchQuery] = useState(""); + // Initialize deterministically (matches SSR) and hydrate from sessionStorage + // after mount to avoid a hydration mismatch that regenerates the whole tree. + const [useLeaflet, setUseLeaflet] = useState(false); + const [mapStyle, setMapStyle] = useState<"street" | "satellite">("street"); + const [hydrated, setHydrated] = useState(false); + const [userMarkers, setUserMarkers] = useState([]); + const [placingMarkerType, setPlacingMarkerType] = useState(null); + const [dragDropEnabled, setDragDropEnabled] = useState(false); + const [pendingClick, setPendingClick] = useState<{ lat: number; lng: number } | null>(null); + const [householdsRefreshKey, setHouseholdsRefreshKey] = useState(0); + const [editingMarker, setEditingMarker] = useState(null); + const [draggedItemId, setDraggedItemId] = useState(null); + const [households, setHouseholds] = useState([]); + const [householdsLoading, setHouseholdsLoading] = useState(true); + const [overview, setOverview] = useState(null); + const [selectedHouseholdId, setSelectedHouseholdId] = useState(null); + const mapInstanceRef = useRef(null); + const mapContainerRef = useRef(null); + + // Require a small drag distance before activating dnd, so a plain click on a + // disclosure item is treated as a tool toggle (not a drag). Without this the + // click is swallowed and the marker tool never activates — the dialog would + // then only appear via drag-drop instead of the click-tool → click-map flow. + const sensors = useSensors( + useSensor(PointerSensor, { activationConstraint: { distance: 8 } }) + ); + + const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL ?? "http://localhost:4000"; + + // Dashboard data — households + aggregated overview, straight from the DB. + useEffect(() => { + const token = localStorage.getItem("auth_token"); + const headers: Record = token ? { Authorization: `Bearer ${token}` } : {}; + + Promise.allSettled([ + fetch(`${BACKEND_URL}/households`, { headers }).then((r) => r.json()), + fetch(`${BACKEND_URL}/households/stats-overview`, { headers }).then((r) => r.json()), + ]).then(([hhResult, overviewResult]) => { + if (hhResult.status === "fulfilled" && Array.isArray(hhResult.value)) { + setHouseholds(hhResult.value as HouseholdRow[]); + } + if (overviewResult.status === "fulfilled" && overviewResult.value?.totals) { + setOverview(overviewResult.value as PovertyOverview); + } + setHouseholdsLoading(false); + }); + }, [householdsRefreshKey]); + + // Load existing markers when map view opens. Households are reused from the + // dashboard fetch above; only POIs need their own request here. + useEffect(() => { + if (!useLeaflet) return; + const token = localStorage.getItem("auth_token"); + const headers: Record = token ? { Authorization: `Bearer ${token}` } : {}; + + fetch(`${BACKEND_URL}/poi`, { headers }) + .then((r) => r.json()) + .catch(() => []) + .then((poiRows: any[]) => { + const hhMarkers: UserMarker[] = households.map((r) => ({ + id: `hh-${r.id}`, + lat: parseFloat(r.latitude), + lng: parseFloat(r.longitude), + type: "marker", + name: r.head_name, + meta: { + poverty_level: r.poverty_level, + family_count: r.family_count, + penghasilan: r.penghasilan, + fuzzy_label: r.fuzzy_label ?? undefined, + fuzzy_score: r.fuzzy_score != null ? Number(r.fuzzy_score) : undefined, + notes: r.notes ?? undefined, + }, + })); + const poiMarkers: UserMarker[] = (Array.isArray(poiRows) ? poiRows : []).map((r) => ({ + id: `poi-${r.id}`, + lat: parseFloat(r.latitude), + lng: parseFloat(r.longitude), + type: r.religion_subtype ?? r.poi_type, + name: r.name, + meta: { + poi_type: r.poi_type, + notes: r.notes ?? undefined, + radius: r.radius_meters ?? 0, + }, + })); + setUserMarkers([...hhMarkers, ...poiMarkers]); + }); + }, [useLeaflet, households]); + + useEffect(() => { + setUseLeaflet(sessionStorage.getItem("poverty_useLeaflet") === "true"); + setMapStyle((sessionStorage.getItem("poverty_mapStyle") as "street" | "satellite") ?? "street"); + setHydrated(true); + }, []); + + useEffect(() => { if (hydrated) sessionStorage.setItem("poverty_useLeaflet", String(useLeaflet)); }, [useLeaflet, hydrated]); + useEffect(() => { if (hydrated) sessionStorage.setItem("poverty_mapStyle", mapStyle); }, [mapStyle, hydrated]); + + function handleDragEnd(event: DragEndEvent) { + const { active, over } = event; + setDraggedItemId(null); + if (over?.id !== "map-canvas") return; + const itemId = (active.data.current as any)?.itemId as string | undefined; + if (!itemId || !mapInstanceRef.current) return; + + // Get drop coordinates in the map container + const nativeEvent = event.activatorEvent as PointerEvent; + const rect = mapContainerRef.current?.getBoundingClientRect(); + if (!rect) return; + + const L = (window as any).L; + if (!L) return; + + const x = nativeEvent.clientX + (event.delta?.x ?? 0) - rect.left; + const y = nativeEvent.clientY + (event.delta?.y ?? 0) - rect.top; + const latLng = mapInstanceRef.current.containerPointToLatLng(L.point(x, y)); + + setPendingClick({ lat: latLng.lat, lng: latLng.lng }); + setPlacingMarkerType(itemId); + } + + const demographics = useMemo(() => { + const householdMarkers = userMarkers.filter((m) => m.type === "marker"); + return { + population: householdMarkers.reduce((sum, h) => sum + (h.meta?.family_count ?? 0), 0), + beneficiaries: householdMarkers.filter((h) => h.meta?.poverty_level === "Extreme" || h.meta?.poverty_level === "Miskin").length, + socialSecurity: householdMarkers.filter((h) => h.meta?.poverty_level === "Rentan").length, + }; + }, [userMarkers]); + + // Default the inspector to the highest-priority household. + const selectedHousehold = useMemo(() => { + if (households.length === 0) return null; + const found = households.find((h) => h.id === selectedHouseholdId); + if (found) return found; + return [...households].sort( + (a, b) => Number(b.fuzzy_score ?? -1) - Number(a.fuzzy_score ?? -1) + )[0]; + }, [households, selectedHouseholdId]); + + async function handleMarkerDragEnd(marker: UserMarker, newLat: number, newLng: number) { + const isHousehold = marker.id.startsWith("hh-"); + const dbId = marker.id.split("-").slice(1).join("-"); + const token = localStorage.getItem("auth_token"); + const headers = { "Content-Type": "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}) }; + + try { + const url = isHousehold ? `${BACKEND_URL}/households/${dbId}` : `${BACKEND_URL}/poi/${dbId}`; + const res = await fetch(url, { + method: "PUT", + headers, + body: JSON.stringify({ latitude: newLat, longitude: newLng }), + }); + if (!res.ok) throw new Error("Gagal memindahkan penanda"); + + setUserMarkers((prev) => + prev.map((m) => + m.id === marker.id ? { ...m, lat: newLat, lng: newLng } : m + ) + ); + toast.success(`Posisi "${marker.name}" berhasil diperbarui`); + if (isHousehold) { + setHouseholdsRefreshKey((k) => k + 1); + } + } catch (err) { + toast.error(err instanceof Error ? err.message : "Gagal memindahkan penanda"); + } + } + + // FULL BLEED MAP LAYOUT + if (useLeaflet) { + return ( + setDraggedItemId((e.active.data.current as any)?.itemId ?? null)} + onDragEnd={handleDragEnd} + onDragCancel={() => setDraggedItemId(null)} + > + setUseLeaflet(false)}> +
+ + {/* Leaflet Map filling the screen */} + { + if (placingMarkerType && !editingMarker) { + setPendingClick({ lat, lng }); + } + }} + onMarkerClick={(marker) => { + if (!placingMarkerType) setEditingMarker(marker); + }} + onMapReady={(map) => { mapInstanceRef.current = map; }} + dragDropEnabled={dragDropEnabled} + onMarkerDragEnd={handleMarkerDragEnd} + /> + + {/* Placing Marker Banner — hidden while the dialog is open */} + {placingMarkerType && !pendingClick && ( +
+ + + Menempatkan {PLACING_LABELS[placingMarkerType] ?? placingMarkerType.replace("-", " ")}... Klik pada peta untuk menentukan posisi + + +
+ )} + + {/* Floating Legend HUD overlay */} + + + {/* Floating Collection Grid Disclosure */} +
+ +
+ + {/* Marker Placement Dialog (households + non-religion POIs) */} + { if (!v) { setPendingClick(null); setPlacingMarkerType(null); } }} + markerType={placingMarkerType ?? "marker"} + pendingClick={pendingClick} + onConfirm={(marker) => { + setUserMarkers((prev) => [...prev, marker]); + setPendingClick(null); + setPlacingMarkerType(null); + if (marker.type === "marker") setHouseholdsRefreshKey((k) => k + 1); + }} + /> + + {/* Religion Place Dialog */} + { if (!v) { setPendingClick(null); setPlacingMarkerType(null); } }} + pendingClick={pendingClick} + onConfirm={(marker) => { + setUserMarkers((prev) => [...prev, marker]); + setPendingClick(null); + setPlacingMarkerType(null); + }} + /> + + {/* Marker Edit/Delete Dialog */} + { if (!v) setEditingMarker(null); }} + marker={editingMarker} + onUpdated={(updated) => { + setUserMarkers((prev) => prev.map((m) => m.id === updated.id ? updated : m)); + setEditingMarker(null); + if (updated.type === "marker") setHouseholdsRefreshKey((k) => k + 1); + }} + onDeleted={(deleted) => { + setUserMarkers((prev) => prev.filter((m) => m.id !== deleted.id)); + setEditingMarker(null); + if (deleted.type === "marker") setHouseholdsRefreshKey((k) => k + 1); + }} + /> +
+
+ + {/* Drag overlay badge */} + + {draggedItemId && ( +
+ {PLACING_LABELS[draggedItemId] ?? draggedItemId.replace("-", " ")} +
+ )} +
+
+ ); + } + + // STANDARD DASHBOARD LAYOUT (DEFAULT) + return ( + +
+ setUseLeaflet(true)} + buttonText="Peta Kemiskinan" + icon={} + /> + + {/* Unified Grid Layout */} +
+ {/* Row 1: 4 stats cards (rendered inside PovertyStatsCards as direct children) */} + + + {/* Row 2: Trend Chart */} +
+ +
+ + {/* Row 2: Inspector Top */} +
+ +
+ + {/* Row 3: Inspector Bottom */} +
+ +
+ + {/* Row 3: Directory */} +
+ setSelectedHouseholdId(h.id)} + /> +
+ + {/* Row 4: Fuzzy Priority Distribution */} + setHouseholdsRefreshKey((k) => k + 1)} + /> + + {/* Row 5: Households Table (full width) */} +
+ +
+
+ +
+
+ ); +} diff --git a/frontend/app/dashboard/settings/page.tsx b/frontend/app/dashboard/settings/page.tsx new file mode 100644 index 0000000..4ae9246 --- /dev/null +++ b/frontend/app/dashboard/settings/page.tsx @@ -0,0 +1,229 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { toast } from "sonner"; +import { AppShell } from "@/components/app-shell"; +import { useAuth } from "@/lib/auth-context"; +import { Input } from "@/components/ui/input"; +import type { Role } from "@/lib/auth-types"; +import { + UserIcon, + AtSignIcon, + KeyRoundIcon, + SettingsIcon +} from "@/components/ui/phosphor-icons"; + +const ROLE_DETAILS = [ + { + value: "superadmin" as Role, + label: "Super Administrator", + description: "Akses penuh ke semua peta (Kemiskinan, Lahan & Jalan, SPBU) serta statistik analitik.", + }, + { + value: "admin_poverty" as Role, + label: "Admin Poverty Map", + description: "Akses terbatas khusus untuk peta kemiskinan dan analisis data warga miskin.", + }, + { + value: "admin_lands_roads" as Role, + label: "Admin Lahan & Jalan", + description: "Akses terbatas khusus untuk manajemen lahan tata ruang dan jalan transportasi.", + }, + { + value: "admin_gas_stations" as Role, + label: "Admin SPBU", + description: "Akses terbatas khusus untuk pemetaan SPBU, EV Charger, dan Bengkel.", + } +]; + +export default function SettingsPage() { + const { user, token, login } = useAuth(); + + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [selectedRole, setSelectedRole] = useState("superadmin"); + const [mapStyle, setMapStyle] = useState<"street" | "satellite">("street"); + const [saving, setSaving] = useState(false); + + // Load user details when loaded + useEffect(() => { + if (user) { + setName(user.username || ""); + setEmail(user.email || ""); + setSelectedRole(user.role || "superadmin"); + + // Load map preference from localStorage if set + const savedMapStyle = localStorage.getItem("default_map_style") as "street" | "satellite"; + if (savedMapStyle) { + setMapStyle(savedMapStyle); + } + } + }, [user]); + + function handleSave(e: React.FormEvent) { + e.preventDefault(); + if (!user || !token) return; + + setSaving(true); + + setTimeout(() => { + const updatedUser = { + ...user, + username: name.trim(), + email: email.trim(), + role: selectedRole + }; + + // Save profile and role to AuthContext + login(token, updatedUser); + + // Save map preferences to localStorage + localStorage.setItem("default_map_style", mapStyle); + + toast.success("Pengaturan berhasil disimpan!"); + setSaving(false); + }, 800); + } + + return ( + +
+ {/* Page Header */} +
+
+
+ +
+

+ Pengaturan Akun +

+
+

+ Kelola informasi profil, peran administrator sistem, dan preferensi tampilan peta global. +

+
+ +
+ {/* Profile Card */} +
+

Informasi Profil

+ +
+
+ + setName(e.target.value)} + placeholder="Masukkan nama pengguna" + required + className="bg-background border border-border focus-visible:border-ring rounded-md h-10 text-sm" + /> +
+ +
+ + setEmail(e.target.value)} + placeholder="Masukkan email" + required + className="bg-background border border-border focus-visible:border-ring rounded-md h-10 text-sm" + /> +
+
+
+ + {/* Role Gating Card */} +
+
+

+ + Peran & Hak Akses +

+

+ Pilih peran admin untuk membatasi atau membuka modul visualisasi peta di sidebar. +

+
+ +
+ {ROLE_DETAILS.map((roleOpt) => { + const isSelected = selectedRole === roleOpt.value; + return ( +
setSelectedRole(roleOpt.value)} + className={`p-4 border rounded-xl cursor-pointer flex flex-col gap-2 transition-all select-none ${ + isSelected + ? "border-primary bg-primary/5 shadow-sm" + : "border-border/60 bg-background hover:bg-muted/50" + }`} + > +
+ + {roleOpt.label} + + {isSelected && ( + + )} +
+

+ {roleOpt.description} +

+
+ ); + })} +
+
+ + {/* Map Preferences Card */} +
+

Preferensi Peta

+ +
+ + Default Map Style + +
+ {["street", "satellite"].map((style) => { + const isSelected = mapStyle === style; + return ( + + ); + })} +
+
+
+ + {/* Submit Action */} +
+ +
+
+
+
+ ); +} diff --git a/frontend/app/favicon.ico b/frontend/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..96ae5b43874393a82e3def56f40f224bea9cf06b GIT binary patch literal 15406 zcmeHNX^d4x7JieNAKeW~(|{t)()`dg3bG|G7>$5{2+CqaV@AXg5+Q0b=p;t{HR?Fx zLV}703}II$M!>Bxg0eG!!;;YYOK91I9+YknMCk4_=Q~xW-nrX*-SBhl$EB)Ho%5aV zoT^*zzPgpm{UP_qTzPqp%c|UCf6C=<$mMcXRjys#F_&AwvH=5p|24VX)-JhRHS4em zTR1rvsedgkEyVu_|DkkkuJeam(&maQ(eGtJ&)R6+X~8Ulr@l}+Z0&cr)a?8@90VE+pj zE)?11$&-WqkRd}tUT(VSCh0$V^e9cb|9+`2Teb}J#t`C z<4H{|T>q72v*~o>X__?2*)LhLSaOP7fPv^!bM^^*#r0*P-{7^XPw@)b74?&(-q~WE>G)IN{SV#RAim^~%uut*h`{H9MF=~9lUin5#YisKvPo7)xKIW%B zrHt`a9;(A@hWOC3P!4RLj;oa=>fX2d(h_>MuAEqIC=ZEc;JD6v+|>r@|`qn*f9F#mtW)>e#adm z`)K7#uSax0e;;0V-F3A6-`j(&a6wMud4W%rF_-h_&(n)9z9`@4`2L$TX_8sWECbYYC>n7jp@N?gN_faY9;F;j1mtLX^7cNA2aKpEw3g1!8G#Zl=5McFzd#oxe&=y@c+%>n>_)Y7+l>of1O64jkAY;lLE!82J3J zUcH*Sbm=0z!5tSc9(?dYdh^XU%Um3`|Rx zE{)`6+&%Y*KlG_zEv#O~vNhkpa$d0hQ#{+Gc&W9c<^ z>{z!>Mf|&aQ2d)1B!1T0ymM84n|u}R&JS(GZ}m}^Ilv|SXrJCGw_sKu2Z|llaF_Y$jG^BWT-Ny(C^!77vd9> zkW<0bzF_NPV|muT!2gRJ`y{+Yo14PdMeUPlZc$=v`4h>x?QdhftvGGfcXK&EkHXwm zp0W92@oha}r}i0T(l(1jmOMXy*#*Pdc1m6EPw3gYx6|jQbetG|EGOwUDIDS)3*gxN zNZoR_^*Cmm+AgkyuS|?s9#Zuzrq%cLEbb~VkdvZ$ZjG(_0r%GEr^c8*hl=vc!YU>Y zJZ(Px{Ic;_oP<2Z*66!AP*^7tUjpCrF_;nkOOS{88GJI&P?u56x2^mVe72f9{OJXM zZSbGD<`l$e!x&v^{FaBUXT_;HT3)LEgnd@*R&MKAbCPwy@nU&~`!Nf@fIQ8o%CywD zwXJ2yyXUWZDz|#JFQp#tDYdm_ioUxa*ssQ}zO;}mAzx=#K#HyAg3BnF*g4;hfn@UELl9_yyE=P4_kd+_xixc z-V^o@*R5OUeEGg}=g!Gq=)i#k>F+PRATjECfxU9<8)JX)t+(DH=xQIzFTea!_K<37 zYTW*U$Fw?Wb%_zWS#cUUa{kOS&rr9@O4;*z_0?A$2d{&@)ZV>&OJ7Ar1)(22e>?XZ zz5e>^)Ty)E$E~WWqW`e2<@3*1UXlIQO2&SU_cal(>&GxKKfN^1+AdUjUYIY~x0*O% zB9)bux&1ocubDM#mcwN|yl1%e)?33~9`=+F!=lBDWuMFL|6%`X$St=-_ETQw{aoFL zD&zgUd+)uMzODUM_R9lz&y}6#abt<#1l`*I}Y%({MEjH|Nived+$kZ;xVvEb__nI`s2rWkNpwq zr2F^m6MhvR^8@==AFTL*uDtR}C+FO1zwXtCwy}i_d;ZIpFBgn7{?LK%M<4wQ)z{Y- z;LEWf?>_f4#6e%Q|x82;qH*au`2;g6ywjk5pw^0%QI9^)e1KX3U6;kLsYr zy0vrX&La88*`9S&S($&Ar*pm|zdKs7Vnvu|xVPjTQVM^J@xFcgWGt{Qp$=IZe{fs1 zYL)ckep;|#f#gN~Y7F3yGf6tnv*r=LmnzM>NVOk4c(8k4>+f%zm(qLGzMr{A-os#+ zTj(&41I{F=&%j?}_NDTN4aWBBtFO+F6YC(Bz~B2X#NWP4RsNW}!o}O;U61zJ((&in zY1Ra-2mZ{*+F1T|bw9|vW!sN>G5(w|@tKu>++Ud2n#)xFoT~_bk14t0c$9y9ZbbRl zrSk8^?-+1CF62Gq{+0QU`GfbPI?fzF)*RvQ^N%xjINK(9f*!`1ZKhA3;e2TR{ktjn z#rFwt?RN4%Yt|2Tiizvm5KSoa@){Bio>hl6<^ zhyi|l32xILn=ZP6AKSJz|KK0?qSN?uu0sA9*ZK(jJr}qBlu*Y`6*4woe6hwoJ9581 zGwZxp{wVPs^x0>h(G7e*m6yAB=c`t)ayDuC_dI&`>`7br++IB2@pe}J!~Dm02lK#M zhBY2%0_yAk>vD|PaCsf6tIU?HaQyf&nmv2AVAR&~`s(|2{wKgTiNVg1Ct?>U5$V~AjCKgL3T2dNCD zUa<3Z3|!rT1F82f@-Js##ZPwr)8;?cl~JSar26Ch9H<Se{f&cpTe1_Nf4Ry^A_c{LVHdfh*=wh*7!5 zd^U-W&!Osgj0+de%Q`u9D4!MbYbVO?!hd}_f5qog=ReLj<2iKh+_}_r!JjKIdLeem zgtwnRo(JX(=7Z>|OiPt>DLN`g>FaoopTlR1s;jHzc^voT*I$3_`TN)nj`G2I2%P13 zYUWdN*2g|`hnxpL4ks~x@SUY&s=A(+`ca;tt>33c5595UQnKpo%V^$;HzBAupSoCQGG3e+sZsg;)T8P)Y4*xdIq1k4eN(-R9i_I z`w4Ad*E|Cw1t%iY{&=aFDRW-^h4HfMfZCx{Ta{(;(f-z!$`$%?%y^&bv=;kf^^o&8 z7saxitSl}!?T}l|A^Pw^iA(3C;uR_lu8y6QD%Y|QMyyYsKVz~8w)QE1ZD*7r-i&@= zi%y&?w-dH;9km5Ee;f`9ZrRJ)*}By!R&T3aYw}Y}2R`F|SDPPOhi@Bi5iS>%) { + return ( + + + + {children} + + + + + ); +} diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx new file mode 100644 index 0000000..64f46f3 --- /dev/null +++ b/frontend/app/page.tsx @@ -0,0 +1,19 @@ +import { LandingHeader } from "@/components/landing/landing-header"; +import { LandingHero } from "@/components/landing/landing-hero"; +import { LandingAbout } from "@/components/landing/landing-about"; +import { LandingProjects } from "@/components/landing/landing-projects"; +import { LandingCta } from "@/components/landing/landing-cta"; +import { LandingFooter } from "@/components/landing/landing-footer"; + +export default function Home() { + return ( +
+ + + + + + +
+ ); +} diff --git a/frontend/bun.lock b/frontend/bun.lock new file mode 100644 index 0000000..4950815 --- /dev/null +++ b/frontend/bun.lock @@ -0,0 +1,1560 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "sig-app", + "dependencies": { + "@base-ui/react": "^1.5.0", + "@dnd-kit/core": "^6.3.1", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^1.17.0", + "motion": "^12.40.0", + "next": "16.2.7", + "next-themes": "^0.4.6", + "react": "19.2.4", + "react-dom": "19.2.4", + "react-icons": "^5.6.0", + "react-use-measure": "^2.1.7", + "recharts": "3.8.0", + "shadcn": "^4.10.0", + "shaders": "^2.5.129", + "sonner": "^2.0.7", + "tailwind-merge": "^3.6.0", + "tw-animate-css": "^1.4.0", + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "16.2.7", + "tailwindcss": "^4", + "typescript": "^5", + }, + }, + }, + "trustedDependencies": [ + "sharp", + "unrs-resolver", + ], + "packages": { + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], + + "@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="], + + "@babel/compat-data": ["@babel/compat-data@7.29.7", "", {}, "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg=="], + + "@babel/core": ["@babel/core@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-module-transforms": "^7.29.7", "@babel/helpers": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA=="], + + "@babel/generator": ["@babel/generator@7.29.7", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ=="], + + "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" } }, "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw=="], + + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.29.7", "", { "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g=="], + + "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.29.7", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-member-expression-to-functions": "^7.29.7", "@babel/helper-optimise-call-expression": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", "@babel/traverse": "^7.29.7", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg=="], + + "@babel/helper-globals": ["@babel/helper-globals@7.29.7", "", {}, "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA=="], + + "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg=="], + + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g=="], + + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.29.7", "", { "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg=="], + + "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" } }, "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong=="], + + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.29.7", "", {}, "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw=="], + + "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.29.7", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.29.7", "@babel/helper-optimise-call-expression": "^7.29.7", "@babel/traverse": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ=="], + + "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.29.7", "", { "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.29.7", "", {}, "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw=="], + + "@babel/helpers": ["@babel/helpers@7.29.7", "", { "dependencies": { "@babel/template": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg=="], + + "@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], + + "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A=="], + + "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA=="], + + "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.29.7", "", { "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ=="], + + "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.29.7", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", "@babel/plugin-syntax-typescript": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw=="], + + "@babel/preset-typescript": ["@babel/preset-typescript@7.29.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "@babel/plugin-syntax-jsx": "^7.29.7", "@babel/plugin-transform-modules-commonjs": "^7.29.7", "@babel/plugin-transform-typescript": "^7.29.7" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ=="], + + "@babel/runtime": ["@babel/runtime@7.29.7", "", {}, "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw=="], + + "@babel/template": ["@babel/template@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7" } }, "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg=="], + + "@babel/traverse": ["@babel/traverse@7.29.7", "", { "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" } }, "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw=="], + + "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + + "@base-ui/react": ["@base-ui/react@1.5.0", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@base-ui/utils": "0.2.9", "@floating-ui/react-dom": "^2.1.8", "@floating-ui/utils": "^0.2.11", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@date-fns/tz": "^1.2.0", "@types/react": "^17 || ^18 || ^19", "date-fns": "^4.0.0", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@date-fns/tz", "@types/react", "date-fns"] }, "sha512-z1gSAlced1yY+iM+mHDEtIkD8UI3Ebs52MuBPxvV6f5hRutk+xvCH/wuB7hDqDzK9JG5FoMz5nhrqtSs1wjt1A=="], + + "@base-ui/utils": ["@base-ui/utils@0.2.9", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@floating-ui/utils": "^0.2.11", "reselect": "^5.1.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-x/PDDCYzoqPpjrdyb3VcyylTI2IjUXEtYDGi5foh7KsnmNJIIaVwA2GLgDH1dps1GgXiJbA60hM+AyuTfQzIvw=="], + + "@dnd-kit/accessibility": ["@dnd-kit/accessibility@3.1.1", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw=="], + + "@dnd-kit/core": ["@dnd-kit/core@6.3.1", "", { "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ=="], + + "@dnd-kit/utilities": ["@dnd-kit/utilities@3.2.2", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg=="], + + "@dotenvx/dotenvx": ["@dotenvx/dotenvx@1.71.0", "", { "dependencies": { "commander": "^11.1.0", "dotenv": "^17.2.1", "eciesjs": "^0.4.10", "enquirer": "^2.4.1", "execa": "^5.1.1", "fdir": "^6.2.0", "ignore": "^5.3.0", "object-treeify": "1.1.33", "picomatch": "^4.0.4", "which": "^4.0.0", "yocto-spinner": "^1.1.0" }, "bin": { "dotenvx": "src/cli/dotenvx.js" } }, "sha512-KEUw/mGu+EDRhYWRTNGHIimVCs9NvMFaIXOGrHSXoCteKLE5EsJnmPjOPpYorjXVg/0xG0fbdVw720azw1z4ag=="], + + "@ecies/ciphers": ["@ecies/ciphers@0.2.6", "", { "peerDependencies": { "@noble/ciphers": "^1.0.0" } }, "sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g=="], + + "@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.11.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], + + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], + + "@eslint/config-array": ["@eslint/config-array@0.21.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.5" } }, "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw=="], + + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], + + "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], + + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.5", "", { "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" } }, "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg=="], + + "@eslint/js": ["@eslint/js@9.39.4", "", {}, "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw=="], + + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], + + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], + + "@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.8", "", { "dependencies": { "@floating-ui/dom": "^1.7.6" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="], + + "@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="], + + "@humanfs/core": ["@humanfs/core@0.19.2", "", { "dependencies": { "@humanfs/types": "^0.15.0" } }, "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA=="], + + "@humanfs/node": ["@humanfs/node@0.16.8", "", { "dependencies": { "@humanfs/core": "^0.19.2", "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ=="], + + "@humanfs/types": ["@humanfs/types@0.15.0", "", {}, "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q=="], + + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], + + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + + "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], + + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], + + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + + "@inquirer/ansi": ["@inquirer/ansi@2.0.7", "", {}, "sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q=="], + + "@inquirer/confirm": ["@inquirer/confirm@6.1.1", "", { "dependencies": { "@inquirer/core": "^11.2.1", "@inquirer/type": "^4.0.7" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ=="], + + "@inquirer/core": ["@inquirer/core@11.2.1", "", { "dependencies": { "@inquirer/ansi": "^2.0.7", "@inquirer/figures": "^2.0.7", "@inquirer/type": "^4.0.7", "cli-width": "^4.1.0", "fast-wrap-ansi": "^0.2.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA=="], + + "@inquirer/figures": ["@inquirer/figures@2.0.7", "", {}, "sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw=="], + + "@inquirer/type": ["@inquirer/type@4.0.7", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="], + + "@mswjs/interceptors": ["@mswjs/interceptors@0.41.9", "", { "dependencies": { "@open-draft/deferred-promise": "^2.2.0", "@open-draft/logger": "^0.3.0", "@open-draft/until": "^2.0.0", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "strict-event-emitter": "^0.5.1" } }, "sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w=="], + + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], + + "@next/env": ["@next/env@16.2.7", "", {}, "sha512-tMJizPlj6ZYpBMMdK8S0LJufrP4QTdR6pcv9KQ/bVETPAmg0j1mlHE9G2c38UyGHxoBapgwuj7XjbGJ2RcDFOg=="], + + "@next/eslint-plugin-next": ["@next/eslint-plugin-next@16.2.7", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-VbS+QgMHqvIDMTIqD2xMBKK1otIpdAUKA8VLHFwR9h6OfU/mOm7w/69nQcvdmI8hCk99Wr2AsGLn/PJ/tMHw1w=="], + + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.2.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vm1EDI/pVaBNNiychmxk3fft+OhQPVD9cIM/tReLZIQ3TfQ4kqI9DwKk00dzuS1ulC7icbrzCFrmRRlk9PfNdw=="], + + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.2.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-O3IRSv1ZBL1zs0WrIgefTEcTKFVn+ryxBNe54erJ6KsD+2f/Mmt7g2jOYh8PSBdUwPtKQJuCsTMlZ7tIu2AcsQ=="], + + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.2.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-Re6PZtjBDd0aMU+VcZcC/PrIvj4WhrjDYtMhhCVQamWN4L90EVP0pcEOBQD25prSlw7OzNw5QpHLWMilRLsRNw=="], + + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.2.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-qyogG9QtBzWxgJfeGBvOEHI3851gTfCF3wLZ5RDLTBJGAmE9p1qDwKCOdrBrvBzRvYDT+gUDp72pzlSEfAXgNA=="], + + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.2.7", "", { "os": "linux", "cpu": "x64" }, "sha512-Vhe4ZDuBpmMogrGi5D4R2Kq4JAQlj6+wvgaFYy31zfES0zPmt6TLA+cuYpM/OLrPZjo2MYQTHVqNUSCR6+fDZQ=="], + + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.2.7", "", { "os": "linux", "cpu": "x64" }, "sha512-srvian89JahFLw1YLBEuhvPJ0DO5lpUeJQMXy4xYo7g628ZlNgXdNkqoxSAv9OYrBfByh6vxISMwW/mRbzCY+g=="], + + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.2.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-GX3wvLpULFuRFJzwHaKfm7QZJ18F4ZSuxlPJ96BoBglCzBmdSjyeBKF+ZhWhvL/ckxNfLnNa7bsObO2ipYpszw=="], + + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.2.7", "", { "os": "win32", "cpu": "x64" }, "sha512-J4WlM72NMk076Qsg0jTdK3SNXatlSdnjW7L7oNGLst1tAGjHrJh/FYi+pw9wyIjEtGRKDNzD0zuiY16oWYWVaw=="], + + "@noble/ciphers": ["@noble/ciphers@1.3.0", "", {}, "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw=="], + + "@noble/curves": ["@noble/curves@1.9.7", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw=="], + + "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + + "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="], + + "@open-draft/deferred-promise": ["@open-draft/deferred-promise@3.0.0", "", {}, "sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA=="], + + "@open-draft/logger": ["@open-draft/logger@0.3.0", "", { "dependencies": { "is-node-process": "^1.2.0", "outvariant": "^1.4.0" } }, "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ=="], + + "@open-draft/until": ["@open-draft/until@2.1.0", "", {}, "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg=="], + + "@reduxjs/toolkit": ["@reduxjs/toolkit@2.12.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^11.0.0", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "optionalPeers": ["react", "react-redux"] }, "sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw=="], + + "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], + + "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], + + "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + + "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="], + + "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.3.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.21.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.0" } }, "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.0", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.0", "@tailwindcss/oxide-darwin-arm64": "4.3.0", "@tailwindcss/oxide-darwin-x64": "4.3.0", "@tailwindcss/oxide-freebsd-x64": "4.3.0", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", "@tailwindcss/oxide-linux-x64-musl": "4.3.0", "@tailwindcss/oxide-wasm32-wasi": "4.3.0", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" } }, "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.0", "", { "os": "android", "cpu": "arm64" }, "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0", "", { "os": "linux", "cpu": "arm" }, "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.0", "", { "dependencies": { "@emnapi/core": "^1.10.0", "@emnapi/runtime": "^1.10.0", "@emnapi/wasi-threads": "^1.2.1", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA=="], + + "@tailwindcss/postcss": ["@tailwindcss/postcss@4.3.0", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.3.0", "@tailwindcss/oxide": "4.3.0", "postcss": "^8.5.10", "tailwindcss": "4.3.0" } }, "sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w=="], + + "@ts-morph/common": ["@ts-morph/common@0.27.0", "", { "dependencies": { "fast-glob": "^3.3.3", "minimatch": "^10.0.1", "path-browserify": "^1.0.1" } }, "sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ=="], + + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + + "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], + + "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], + + "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="], + + "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="], + + "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="], + + "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="], + + "@types/d3-shape": ["@types/d3-shape@3.1.8", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="], + + "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="], + + "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="], + + "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], + + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], + + "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], + + "@types/node": ["@types/node@20.19.42", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-5L7SUaFC1RyDraj2yRhyBzHTobyXHmohD100CChNtyPyleoq37Mqab5Gn8XEKI04dfN/oqPdpHk38MgcQWHbZg=="], + + "@types/react": ["@types/react@19.2.17", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw=="], + + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], + + "@types/set-cookie-parser": ["@types/set-cookie-parser@2.4.10", "", { "dependencies": { "@types/node": "*" } }, "sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw=="], + + "@types/statuses": ["@types/statuses@2.0.6", "", {}, "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA=="], + + "@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="], + + "@types/validate-npm-package-name": ["@types/validate-npm-package-name@4.0.2", "", {}, "sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw=="], + + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.60.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.60.1", "@typescript-eslint/type-utils": "8.60.1", "@typescript-eslint/utils": "8.60.1", "@typescript-eslint/visitor-keys": "8.60.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.60.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg=="], + + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.60.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.60.1", "@typescript-eslint/types": "8.60.1", "@typescript-eslint/typescript-estree": "8.60.1", "@typescript-eslint/visitor-keys": "8.60.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA=="], + + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.60.1", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.60.1", "@typescript-eslint/types": "^8.60.1", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw=="], + + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.60.1", "", { "dependencies": { "@typescript-eslint/types": "8.60.1", "@typescript-eslint/visitor-keys": "8.60.1" } }, "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w=="], + + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.60.1", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA=="], + + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.60.1", "", { "dependencies": { "@typescript-eslint/types": "8.60.1", "@typescript-eslint/typescript-estree": "8.60.1", "@typescript-eslint/utils": "8.60.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A=="], + + "@typescript-eslint/types": ["@typescript-eslint/types@8.60.1", "", {}, "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w=="], + + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.60.1", "", { "dependencies": { "@typescript-eslint/project-service": "8.60.1", "@typescript-eslint/tsconfig-utils": "8.60.1", "@typescript-eslint/types": "8.60.1", "@typescript-eslint/visitor-keys": "8.60.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew=="], + + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.60.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.60.1", "@typescript-eslint/types": "8.60.1", "@typescript-eslint/typescript-estree": "8.60.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg=="], + + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.60.1", "", { "dependencies": { "@typescript-eslint/types": "8.60.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag=="], + + "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.12.2", "", { "os": "android", "cpu": "arm" }, "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w=="], + + "@unrs/resolver-binding-android-arm64": ["@unrs/resolver-binding-android-arm64@1.12.2", "", { "os": "android", "cpu": "arm64" }, "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ=="], + + "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.12.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w=="], + + "@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.12.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA=="], + + "@unrs/resolver-binding-freebsd-x64": ["@unrs/resolver-binding-freebsd-x64@1.12.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg=="], + + "@unrs/resolver-binding-linux-arm-gnueabihf": ["@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2", "", { "os": "linux", "cpu": "arm" }, "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A=="], + + "@unrs/resolver-binding-linux-arm-musleabihf": ["@unrs/resolver-binding-linux-arm-musleabihf@1.12.2", "", { "os": "linux", "cpu": "arm" }, "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g=="], + + "@unrs/resolver-binding-linux-arm64-gnu": ["@unrs/resolver-binding-linux-arm64-gnu@1.12.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg=="], + + "@unrs/resolver-binding-linux-arm64-musl": ["@unrs/resolver-binding-linux-arm64-musl@1.12.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA=="], + + "@unrs/resolver-binding-linux-loong64-gnu": ["@unrs/resolver-binding-linux-loong64-gnu@1.12.2", "", { "os": "linux", "cpu": "none" }, "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q=="], + + "@unrs/resolver-binding-linux-loong64-musl": ["@unrs/resolver-binding-linux-loong64-musl@1.12.2", "", { "os": "linux", "cpu": "none" }, "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew=="], + + "@unrs/resolver-binding-linux-ppc64-gnu": ["@unrs/resolver-binding-linux-ppc64-gnu@1.12.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg=="], + + "@unrs/resolver-binding-linux-riscv64-gnu": ["@unrs/resolver-binding-linux-riscv64-gnu@1.12.2", "", { "os": "linux", "cpu": "none" }, "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A=="], + + "@unrs/resolver-binding-linux-riscv64-musl": ["@unrs/resolver-binding-linux-riscv64-musl@1.12.2", "", { "os": "linux", "cpu": "none" }, "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w=="], + + "@unrs/resolver-binding-linux-s390x-gnu": ["@unrs/resolver-binding-linux-s390x-gnu@1.12.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw=="], + + "@unrs/resolver-binding-linux-x64-gnu": ["@unrs/resolver-binding-linux-x64-gnu@1.12.2", "", { "os": "linux", "cpu": "x64" }, "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ=="], + + "@unrs/resolver-binding-linux-x64-musl": ["@unrs/resolver-binding-linux-x64-musl@1.12.2", "", { "os": "linux", "cpu": "x64" }, "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A=="], + + "@unrs/resolver-binding-openharmony-arm64": ["@unrs/resolver-binding-openharmony-arm64@1.12.2", "", { "os": "none", "cpu": "arm64" }, "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ=="], + + "@unrs/resolver-binding-wasm32-wasi": ["@unrs/resolver-binding-wasm32-wasi@1.12.2", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A=="], + + "@unrs/resolver-binding-win32-arm64-msvc": ["@unrs/resolver-binding-win32-arm64-msvc@1.12.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g=="], + + "@unrs/resolver-binding-win32-ia32-msvc": ["@unrs/resolver-binding-win32-ia32-msvc@1.12.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g=="], + + "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.12.2", "", { "os": "win32", "cpu": "x64" }, "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA=="], + + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + + "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + + "ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], + + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + + "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], + + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], + + "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="], + + "array-includes": ["array-includes@3.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="], + + "array.prototype.findlast": ["array.prototype.findlast@1.2.5", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ=="], + + "array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-shim-unscopables": "^1.1.0" } }, "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ=="], + + "array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="], + + "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="], + + "array.prototype.tosorted": ["array.prototype.tosorted@1.1.4", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3", "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA=="], + + "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], + + "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="], + + "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="], + + "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], + + "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], + + "axe-core": ["axe-core@4.12.0", "", {}, "sha512-FTavr/7Ba0IptwGOPxnQvdyW2tAsdLBMTBXz7rKH6xJ2skpyxpBxyHkDdBs4lf69yRqYpkqCdfhnwS8YULGOmg=="], + + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.34", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-IMDedajPifLnHNY0X9n8hKxRTQ6/eTHwr5bDo04WnuqxyKw6LYtQywCuuqPZwhl3aBXMvQpJov42GLCwRRdQzw=="], + + "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], + + "brace-expansion": ["brace-expansion@1.1.15", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], + + "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], + + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "call-bind": ["call-bind@1.0.9", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], + + "caniuse-lite": ["caniuse-lite@1.0.30001797", "", {}, "sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + + "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], + + "cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], + + "cli-width": ["cli-width@4.1.0", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="], + + "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], + + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "code-block-writer": ["code-block-writer@13.0.3", "", {}, "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + + "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + + "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + + "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + + "cosmiconfig": ["cosmiconfig@9.0.2", "", { "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "peerDependencies": { "typescript": ">=4.9.5" }, "optionalPeers": ["typescript"] }, "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], + + "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], + + "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="], + + "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="], + + "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], + + "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], + + "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], + + "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="], + + "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="], + + "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="], + + "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], + + "damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="], + + "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], + + "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="], + + "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="], + + "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="], + + "dedent": ["dedent@1.7.2", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA=="], + + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], + + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], + + "default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="], + + "default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="], + + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="], + + "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="], + + "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="], + + "dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "eciesjs": ["eciesjs@0.4.18", "", { "dependencies": { "@ecies/ciphers": "^0.2.5", "@noble/ciphers": "^1.3.0", "@noble/curves": "^1.9.7", "@noble/hashes": "^1.8.0" } }, "sha512-wG99Zcfcys9fZux7Cft8BAX/YrOJLJSZ3jyYPfhZHqN2E+Ffx+QXBDsv3gubEgPtV6dTzJMSQUwk1H98/t/0wQ=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "electron-to-chromium": ["electron-to-chromium@1.5.368", "", {}, "sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw=="], + + "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "enhanced-resolve": ["enhanced-resolve@5.23.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA=="], + + "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], + + "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], + + "error-ex": ["error-ex@1.3.4", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ=="], + + "es-abstract": ["es-abstract@1.24.2", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-iterator-helpers": ["es-iterator-helpers@1.3.2", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.2", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", "math-intrinsics": "^1.1.0" } }, "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw=="], + + "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="], + + "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], + + "es-toolkit": ["es-toolkit@1.47.0", "", {}, "sha512-n1GuoD0WEQZMBk5tttoZSqwgyLx01oqa5XsBmCHwPyNe1S9jPBEmtR2pSgp2kJuWE3ciFZ6yRHmY4pM4C3OOkw=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "eslint": ["eslint@9.39.4", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.5", "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ=="], + + "eslint-config-next": ["eslint-config-next@16.2.7", "", { "dependencies": { "@next/eslint-plugin-next": "16.2.7", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^7.0.0", "globals": "16.4.0", "typescript-eslint": "^8.46.0" }, "peerDependencies": { "eslint": ">=9.0.0", "typescript": ">=3.3.1" }, "optionalPeers": ["typescript"] }, "sha512-CQ2aNXkrsjaGA2oJBE1LYnlRdphIAQE9ZQfX9hSv1PNGPyiOMSaVeBfTIO29QxYz+ij/hZudK0cfpCG1HXWstg=="], + + "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.10", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.16.1", "resolve": "^2.0.0-next.6" } }, "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ=="], + + "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.1", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.13", "unrs-resolver": "^1.6.2" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import", "eslint-plugin-import-x"] }, "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ=="], + + "eslint-module-utils": ["eslint-module-utils@2.13.0", "", { "dependencies": { "debug": "^3.2.7" } }, "sha512-bLohSkT6469rRs8czj0tLTD8vaeIS/whvPRJVjDr7IuoTT1k5DYDERlNycjDj/HkOlvQdYurmfZ/g3fG5bgeLQ=="], + + "eslint-plugin-import": ["eslint-plugin-import@2.32.0", "", { "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", "array.prototype.findlastindex": "^1.2.6", "array.prototype.flat": "^1.3.3", "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", "object.values": "^1.2.1", "semver": "^6.3.1", "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA=="], + + "eslint-plugin-jsx-a11y": ["eslint-plugin-jsx-a11y@6.10.2", "", { "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", "axe-core": "^4.10.0", "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "safe-regex-test": "^1.0.3", "string.prototype.includes": "^2.0.1" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q=="], + + "eslint-plugin-react": ["eslint-plugin-react@7.37.5", "", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA=="], + + "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.1.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g=="], + + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], + + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], + + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], + + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + + "esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="], + + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], + + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], + + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="], + + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], + + "eventsource-parser": ["eventsource-parser@3.1.0", "", {}, "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg=="], + + "execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="], + + "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], + + "express-rate-limit": ["express-rate-limit@8.5.2", "", { "dependencies": { "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A=="], + + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], + + "fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="], + + "fast-string-width": ["fast-string-width@3.0.2", "", { "dependencies": { "fast-string-truncated-width": "^3.0.2" } }, "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg=="], + + "fast-uri": ["fast-uri@3.1.2", "", {}, "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ=="], + + "fast-wrap-ansi": ["fast-wrap-ansi@0.2.2", "", { "dependencies": { "fast-string-width": "^3.0.2" } }, "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q=="], + + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], + + "figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="], + + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], + + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], + + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], + + "flatted": ["flatted@3.4.2", "", {}, "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA=="], + + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], + + "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], + + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "framer-motion": ["framer-motion@12.40.0", "", { "dependencies": { "motion-dom": "^12.40.0", "motion-utils": "^12.39.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-uaBd3qC1v3KQqBEjwTUd183K6PbS+j0yR9w9VmEOLWA/tnUcSn8Xa3uck7t4dgpDoUss8xQTcj8W2L07lrnLFg=="], + + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + + "fs-extra": ["fs-extra@11.3.5", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="], + + "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="], + + "fuzzysort": ["fuzzysort@3.1.0", "", {}, "sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ=="], + + "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], + + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-own-enumerable-keys": ["get-own-enumerable-keys@1.0.0", "", {}, "sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="], + + "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], + + "get-tsconfig": ["get-tsconfig@4.14.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA=="], + + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + + "globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="], + + "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "graphql": ["graphql@16.14.1", "", {}, "sha512-cQOsSMS/IrDz82PVyRDvf/Q1F/bRbBVjJlh+xYOkI1qw2bWRvWGiWc+m2O0d6l4Bt1fyY+8kzJ8JFWGJqNeDBg=="], + + "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + + "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], + + "headers-polyfill": ["headers-polyfill@5.0.1", "", { "dependencies": { "@types/set-cookie-parser": "^2.4.10", "set-cookie-parser": "^3.0.1" } }, "sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA=="], + + "hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], + + "hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], + + "hono": ["hono@4.12.24", "", {}, "sha512-I36D1s+HgQc55KbhEr4iybfxv/9o1zdpw+XEM6dJa91LqQD0HCoSGdxpRJCZE+aavs87j4V3Ls2OJzq8C/U4iw=="], + + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + + "human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="], + + "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "immer": ["immer@10.2.0", "", {}, "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw=="], + + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + + "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], + + "ip-address": ["ip-address@10.2.0", "", {}, "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + + "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], + + "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + + "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], + + "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="], + + "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="], + + "is-bun-module": ["is-bun-module@2.0.0", "", { "dependencies": { "semver": "^7.7.1" } }, "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ=="], + + "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], + + "is-core-module": ["is-core-module@2.16.2", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="], + + "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="], + + "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="], + + "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], + + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], + + "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], + + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], + + "is-in-ssh": ["is-in-ssh@1.0.0", "", {}, "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw=="], + + "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="], + + "is-interactive": ["is-interactive@2.0.0", "", {}, "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="], + + "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], + + "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="], + + "is-node-process": ["is-node-process@1.2.0", "", {}, "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw=="], + + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + + "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], + + "is-obj": ["is-obj@3.0.0", "", {}, "sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ=="], + + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], + + "is-regexp": ["is-regexp@3.1.0", "", {}, "sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA=="], + + "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="], + + "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="], + + "is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="], + + "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="], + + "is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="], + + "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + + "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], + + "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], + + "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], + + "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="], + + "is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="], + + "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="], + + "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], + + "jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "js-yaml": ["js-yaml@4.2.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], + + "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], + + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + + "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="], + + "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="], + + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], + + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + + "language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="], + + "language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="], + + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + + "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], + + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], + + "log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "lucide-react": ["lucide-react@1.17.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9FA9evdox/JQL5PT57fdA1x/yg8T7knJ98+zjTL3UfKza6pflQUUh3XtaQIHKvnsJw1lmsEyHVlt5jchYxOQ5w=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + + "merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="], + + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], + + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + + "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + + "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], + + "minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "motion": ["motion@12.40.0", "", { "dependencies": { "framer-motion": "^12.40.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-yjrHUrBFW6kQvjJwRsoiPSAhC5tRwRqNGJWmiJ4CrGnbKp0V88AdzkhBmDoqIsIPfarOe0Uddd37Xq43/gIocA=="], + + "motion-dom": ["motion-dom@12.40.0", "", { "dependencies": { "motion-utils": "^12.39.0" } }, "sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg=="], + + "motion-utils": ["motion-utils@12.39.0", "", {}, "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "msw": ["msw@2.14.6", "", { "dependencies": { "@inquirer/confirm": "^6.0.11", "@mswjs/interceptors": "^0.41.3", "@open-draft/deferred-promise": "^3.0.0", "@types/statuses": "^2.0.6", "cookie": "^1.1.1", "graphql": "^16.13.2", "headers-polyfill": "^5.0.1", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "path-to-regexp": "^6.3.0", "picocolors": "^1.1.1", "rettime": "^0.11.11", "statuses": "^2.0.2", "strict-event-emitter": "^0.5.1", "tough-cookie": "^6.0.1", "type-fest": "^5.5.0", "until-async": "^3.0.2", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": ">= 4.8.x" }, "optionalPeers": ["typescript"], "bin": { "msw": "cli/index.js" } }, "sha512-ALe+N10S72cyx94cMcy3Zs4HhXCj35sgeAL4c+WTvKi0zWnbd8/h0lcFqv0mb2P+aSgAdD7p9HzvA0DiUPxsyg=="], + + "mute-stream": ["mute-stream@3.0.0", "", {}, "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw=="], + + "nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], + + "napi-postinstall": ["napi-postinstall@0.3.4", "", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ=="], + + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], + + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "next": ["next@16.2.7", "", { "dependencies": { "@next/env": "16.2.7", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.2.7", "@next/swc-darwin-x64": "16.2.7", "@next/swc-linux-arm64-gnu": "16.2.7", "@next/swc-linux-arm64-musl": "16.2.7", "@next/swc-linux-x64-gnu": "16.2.7", "@next/swc-linux-x64-musl": "16.2.7", "@next/swc-win32-arm64-msvc": "16.2.7", "@next/swc-win32-x64-msvc": "16.2.7", "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-eMJxgjRzBaj3olkP4cBamHDXL79A8FC6u1GcsO1D1Tsx8bw/LLXUJCaoajVxtnhD3A1IJqIT8IcRJjgBIPJq4w=="], + + "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], + + "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], + + "node-exports-info": ["node-exports-info@1.6.0", "", { "dependencies": { "array.prototype.flatmap": "^1.3.3", "es-errors": "^1.3.0", "object.entries": "^1.1.9", "semver": "^6.3.1" } }, "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw=="], + + "node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], + + "node-releases": ["node-releases@2.0.47", "", {}, "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og=="], + + "npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], + + "object-treeify": ["object-treeify@1.1.33", "", {}, "sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A=="], + + "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], + + "object.entries": ["object.entries@1.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.1" } }, "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw=="], + + "object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="], + + "object.groupby": ["object.groupby@1.0.3", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2" } }, "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ=="], + + "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + + "open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="], + + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + + "ora": ["ora@8.2.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="], + + "outvariant": ["outvariant@1.4.3", "", {}, "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA=="], + + "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], + + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], + + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], + + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], + + "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + + "parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="], + + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + + "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], + + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], + + "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], + + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + + "postcss": ["postcss@8.5.15", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="], + + "postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + + "powershell-utils": ["powershell-utils@0.1.0", "", {}, "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A=="], + + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], + + "pretty-ms": ["pretty-ms@9.3.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="], + + "prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="], + + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "qs": ["qs@6.15.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw=="], + + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + + "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], + + "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], + + "react-icons": ["react-icons@5.6.0", "", { "peerDependencies": { "react": "*" } }, "sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA=="], + + "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "react-redux": ["react-redux@9.3.0", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "@types/react": "^18.2.25 || ^19", "react": "^18.0 || ^19", "redux": "^5.0.0" }, "optionalPeers": ["@types/react", "redux"] }, "sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g=="], + + "react-use-measure": ["react-use-measure@2.1.7", "", { "peerDependencies": { "react": ">=16.13", "react-dom": ">=16.13" }, "optionalPeers": ["react-dom"] }, "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg=="], + + "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], + + "recharts": ["recharts@3.8.0", "", { "dependencies": { "@reduxjs/toolkit": "^1.9.0 || 2.x.x", "clsx": "^2.1.1", "decimal.js-light": "^2.5.1", "es-toolkit": "^1.39.3", "eventemitter3": "^5.0.1", "immer": "^10.1.1", "react-redux": "8.x.x || 9.x.x", "reselect": "5.1.1", "tiny-invariant": "^1.3.3", "use-sync-external-store": "^1.2.2", "victory-vendor": "^37.0.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Z/m38DX3L73ExO4Tpc9/iZWHmHnlzWG4njQbxsF5aSjwqmHNDDIm0rdEBArkwsBvR8U6EirlEHiQNYWCVh9sGQ=="], + + "redux": ["redux@5.0.1", "", {}, "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="], + + "redux-thunk": ["redux-thunk@3.1.0", "", { "peerDependencies": { "redux": "^5.0.0" } }, "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw=="], + + "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], + + "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + + "reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], + + "resolve": ["resolve@2.0.0-next.7", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.2", "node-exports-info": "^1.6.0", "object-keys": "^1.1.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ=="], + + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + + "rettime": ["rettime@0.11.11", "", {}, "sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + + "run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="], + + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + + "safe-array-concat": ["safe-array-concat@1.1.4", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "get-intrinsic": "^1.3.0", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg=="], + + "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="], + + "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + + "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], + + "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + + "set-cookie-parser": ["set-cookie-parser@3.1.0", "", {}, "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw=="], + + "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], + + "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], + + "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "shadcn": ["shadcn@4.10.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/plugin-transform-typescript": "^7.28.0", "@babel/preset-typescript": "^7.27.1", "@dotenvx/dotenvx": "^1.48.4", "@modelcontextprotocol/sdk": "^1.26.0", "@types/validate-npm-package-name": "^4.0.2", "browserslist": "^4.26.2", "commander": "^14.0.0", "cosmiconfig": "^9.0.0", "dedent": "^1.6.0", "deepmerge": "^4.3.1", "diff": "^8.0.2", "execa": "^9.6.0", "fast-glob": "^3.3.3", "fs-extra": "^11.3.1", "fuzzysort": "^3.1.0", "https-proxy-agent": "^7.0.6", "kleur": "^4.1.5", "msw": "^2.10.4", "node-fetch": "^3.3.2", "open": "^11.0.0", "ora": "^8.2.0", "postcss": "^8.5.6", "postcss-selector-parser": "^7.1.0", "prompts": "^2.4.2", "recast": "^0.23.11", "stringify-object": "^5.0.0", "tailwind-merge": "^3.0.1", "ts-morph": "^26.0.0", "tsconfig-paths": "^4.2.0", "validate-npm-package-name": "^7.0.1", "zod": "^3.24.1", "zod-to-json-schema": "^3.24.6" }, "bin": { "shadcn": "dist/index.js" } }, "sha512-84IJhUsK0xqSCRJx3QxyZe2NpUXj2Nwk8Vc8Ow/tCOND3yz4CT6uU4655vqicNXhzG9Q1cyUt+TBl2SiCJwNgg=="], + + "shaders": ["shaders@2.5.129", "", { "dependencies": { "three": "^0.184.0" }, "peerDependencies": { "pixi.js": "^8.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.8.0", "svelte": "^5", "vue": "^3.5.0" }, "optionalPeers": ["pixi.js", "react", "react-dom", "solid-js", "svelte", "vue"] }, "sha512-AH5LxkTT4FTOvHVkQ9FSpeyJ2hgbjrWgPr/Sw7JVZKwl174fqQysIDgGv7F631FlgxoZdoNgrhFL9WBcrxF7MA=="], + + "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], + + "sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="], + + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="], + + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + + "stdin-discarder": ["stdin-discarder@0.2.2", "", {}, "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ=="], + + "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], + + "strict-event-emitter": ["strict-event-emitter@0.5.1", "", {}, "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ=="], + + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "string.prototype.includes": ["string.prototype.includes@2.0.1", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3" } }, "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg=="], + + "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="], + + "string.prototype.repeat": ["string.prototype.repeat@1.0.0", "", { "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w=="], + + "string.prototype.trim": ["string.prototype.trim@1.2.11", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.2", "es-object-atoms": "^1.1.2", "has-property-descriptors": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w=="], + + "string.prototype.trimend": ["string.prototype.trimend@1.0.10", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.2" } }, "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw=="], + + "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], + + "stringify-object": ["stringify-object@5.0.0", "", { "dependencies": { "get-own-enumerable-keys": "^1.0.0", "is-obj": "^3.0.0", "is-regexp": "^3.1.0" } }, "sha512-zaJYxz2FtcMb4f+g60KsRNFOpVMUyuJgA51Zi5Z1DOTC3S59+OQiVOzE9GZt0x72uBGWKsQIuBKeF9iusmKFsg=="], + + "strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + + "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], + + "strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="], + + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], + + "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="], + + "tailwind-merge": ["tailwind-merge@3.6.0", "", {}, "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w=="], + + "tailwindcss": ["tailwindcss@4.3.0", "", {}, "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q=="], + + "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], + + "three": ["three@0.184.0", "", {}, "sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg=="], + + "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], + + "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], + + "tldts": ["tldts@7.4.2", "", { "dependencies": { "tldts-core": "^7.4.2" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-kCwffuaH8ntKtygnWe1b4BJKWiCUH30n5KfoTr6IchcXOwR7chAOFJxFrH3vjANafUYrIA4a7SDL+nn7SiR4Sw=="], + + "tldts-core": ["tldts-core@7.4.2", "", {}, "sha512-nwEyF4vl4RSJjwSjBUmOSxc3BFPoIFdlRthJ6e+5v9P3bHNsoD06UjuqMUspqp7vsEZ1beaHi1km+optiE17yA=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + + "tough-cookie": ["tough-cookie@6.0.1", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw=="], + + "ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="], + + "ts-morph": ["ts-morph@26.0.0", "", { "dependencies": { "@ts-morph/common": "~0.27.0", "code-block-writer": "^13.0.3" } }, "sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug=="], + + "tsconfig-paths": ["tsconfig-paths@4.2.0", "", { "dependencies": { "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], + + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], + + "type-fest": ["type-fest@5.7.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg=="], + + "type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="], + + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], + + "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], + + "typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.9" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="], + + "typed-array-length": ["typed-array-length@1.0.8", "", { "dependencies": { "call-bind": "^1.0.9", "for-each": "^0.3.5", "gopd": "^1.2.0", "is-typed-array": "^1.1.15", "possible-typed-array-names": "^1.1.0", "reflect.getprototypeof": "^1.0.10" } }, "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "typescript-eslint": ["typescript-eslint@8.60.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.60.1", "@typescript-eslint/parser": "8.60.1", "@typescript-eslint/typescript-estree": "8.60.1", "@typescript-eslint/utils": "8.60.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA=="], + + "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="], + + "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], + + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + + "unrs-resolver": ["unrs-resolver@1.12.2", "", { "dependencies": { "napi-postinstall": "^0.3.4" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.12.2", "@unrs/resolver-binding-android-arm64": "1.12.2", "@unrs/resolver-binding-darwin-arm64": "1.12.2", "@unrs/resolver-binding-darwin-x64": "1.12.2", "@unrs/resolver-binding-freebsd-x64": "1.12.2", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", "@unrs/resolver-binding-linux-x64-musl": "1.12.2", "@unrs/resolver-binding-openharmony-arm64": "1.12.2", "@unrs/resolver-binding-wasm32-wasi": "1.12.2", "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" } }, "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ=="], + + "until-async": ["until-async@3.0.2", "", {}, "sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw=="], + + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + + "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "validate-npm-package-name": ["validate-npm-package-name@7.0.2", "", {}, "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A=="], + + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + + "victory-vendor": ["victory-vendor@37.3.6", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ=="], + + "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="], + + "which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="], + + "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="], + + "which-typed-array": ["which-typed-array@1.1.22", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.9", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw=="], + + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], + + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "wsl-utils": ["wsl-utils@0.3.1", "", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + + "yocto-spinner": ["yocto-spinner@1.2.0", "", { "dependencies": { "yoctocolors": "^2.1.1" } }, "sha512-Yw0hUB6UA3o4YUgKy3oSe9a4cxoaZ9sBfYDw+JSxo6Id0KoJGoxzPA24qqUXYKBWABs/zDSGTz9kww7t3F0XGw=="], + + "yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="], + + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + + "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], + + "zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="], + + "@base-ui/utils/reselect": ["reselect@5.2.0", "", {}, "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw=="], + + "@dotenvx/dotenvx/commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], + + "@dotenvx/dotenvx/execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], + + "@dotenvx/dotenvx/which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="], + + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + + "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + + "@modelcontextprotocol/sdk/ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], + + "@modelcontextprotocol/sdk/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + + "@mswjs/interceptors/@open-draft/deferred-promise": ["@open-draft/deferred-promise@2.2.0", "", {}, "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA=="], + + "@next/eslint-plugin-next/fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="], + + "@reduxjs/toolkit/immer": ["immer@11.1.8", "", {}, "sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA=="], + + "@reduxjs/toolkit/reselect": ["reselect@5.2.0", "", {}, "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@ts-morph/common/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], + + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], + + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="], + + "@typescript-eslint/typescript-estree/semver": ["semver@7.8.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ=="], + + "@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], + + "@unrs/resolver-binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + + "ajv-formats/ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], + + "cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "enquirer/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + + "eslint-plugin-import/tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="], + + "eslint-plugin-react-hooks/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], + + "express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "is-bun-module/semver": ["semver@7.8.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ=="], + + "log-symbols/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], + + "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], + + "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], + + "ora/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], + + "restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + + "router/path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], + + "sharp/semver": ["semver@7.8.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ=="], + + "string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + + "type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], + + "wrap-ansi/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "@dotenvx/dotenvx/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + + "@dotenvx/dotenvx/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], + + "@dotenvx/dotenvx/execa/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], + + "@dotenvx/dotenvx/execa/npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], + + "@dotenvx/dotenvx/execa/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "@dotenvx/dotenvx/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + + "@dotenvx/dotenvx/which/isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], + + "@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "@next/eslint-plugin-next/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "@ts-morph/common/minimatch/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="], + + "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "enquirer/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "eslint-plugin-import/tsconfig-paths/json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], + + "wrap-ansi/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "@ts-morph/common/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + } +} diff --git a/frontend/components.json b/frontend/components.json new file mode 100644 index 0000000..1216e3c --- /dev/null +++ b/frontend/components.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "base-nova", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "rtl": false, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "menuColor": "default", + "menuAccent": "subtle", + "registries": { + "@efferd": "https://efferd.com/r/{style}/{name}.json" + } +} diff --git a/frontend/components/app-breadcrumbs.tsx b/frontend/components/app-breadcrumbs.tsx new file mode 100644 index 0000000..97ff782 --- /dev/null +++ b/frontend/components/app-breadcrumbs.tsx @@ -0,0 +1,32 @@ +import type { ReactNode } from "react"; +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbList, + BreadcrumbPage, +} from "@/components/ui/breadcrumb"; + +/** Current page segment shown in the header — pass a nav item or `{ title, icon? }`. */ +export type AppBreadcrumbPage = { + title: string; + icon?: ReactNode; +}; + +export function AppBreadcrumbs({ page }: { page?: AppBreadcrumbPage | null }) { + if (!page?.title) { + return null; + } + + return ( + + + + + {page.icon} + {page.title} + + + + + ); +} diff --git a/frontend/components/app-header.tsx b/frontend/components/app-header.tsx new file mode 100644 index 0000000..4e4f0b2 --- /dev/null +++ b/frontend/components/app-header.tsx @@ -0,0 +1,47 @@ +import { cn } from "@/lib/utils"; +import { Separator } from "@/components/ui/separator"; +import { DecorIcon } from "@/components/decor-icon"; +import { AppBreadcrumbs } from "@/components/app-breadcrumbs"; +import { MapsBreadcrumb } from "@/components/maps/maps-breadcrumb"; +import { getNavLinks } from "@/components/app-shared"; +import { CustomSidebarTrigger } from "@/components/custom-sidebar-trigger"; +import { NavUser } from "@/components/nav-user"; +import { TrendingDownIcon, RoadHorizonIcon, FuelIcon } from "@/components/ui/phosphor-icons"; + +const MAP_PAGES: Record = { + "/dashboard/poverty": { label: "Peta Kemiskinan", icon: }, + "/dashboard/land": { label: "Lahan dan Jalan", icon: }, + "/dashboard/gas-station": { label: "SPBU dan Charger", icon: }, +}; + +export function AppHeader({ activePath, fullBleed = false, onExitFullBleed }: { activePath?: string; fullBleed?: boolean; onExitFullBleed?: () => void }) { + const links = getNavLinks(activePath); + const activeItem = links.find((item) => item.isActive); + const mapPage = activePath ? MAP_PAGES[activePath] : undefined; + + return ( +
+ +
+ + + {mapPage ? ( + + ) : ( + + )} +
+
+ +
+
+ ); +} diff --git a/frontend/components/app-shared.tsx b/frontend/components/app-shared.tsx new file mode 100644 index 0000000..74e44a3 --- /dev/null +++ b/frontend/components/app-shared.tsx @@ -0,0 +1,121 @@ +import type { ReactNode } from "react"; +import { + FuelIcon, + SettingsIcon, + HelpCircleIcon, + BookOpenIcon, + TrendingDownIcon, + RoadHorizonIcon, + HouseIcon +} from "@/components/ui/phosphor-icons"; + +export type SidebarNavItem = { + title: string; + path?: string; + icon?: ReactNode; + isActive?: boolean; + subItems?: SidebarNavItem[]; +}; + +export type SidebarNavGroup = { + label?: string; + items: SidebarNavItem[]; +}; + +const ROLE_ALLOWED_MAP_PATHS: Record = { + superadmin: ["/dashboard/poverty", "/dashboard/land", "/dashboard/gas-station"], + admin_poverty: ["/dashboard/poverty"], + admin_lands_roads: ["/dashboard/land"], + admin_gas_stations: ["/dashboard/gas-station"], +}; + +const checkIsActive = (activePath?: string, itemPath?: string): boolean => { + if (!activePath || !itemPath) return false; + if (itemPath.startsWith("#")) return false; // Ignore hash placeholders + if (itemPath === "/dashboard") { + return activePath === "/dashboard"; + } + return activePath === itemPath || activePath.startsWith(itemPath + "/"); +}; + +export const getNavGroups = (activePath?: string, role?: string): SidebarNavGroup[] => { + const allowedMapPaths = role ? (ROLE_ALLOWED_MAP_PATHS[role] ?? []) : null; + + const allMapItems = [ + { + title: "Peta Kemiskinan", + path: "/dashboard/poverty", + icon: , + }, + { + title: "Lahan & Jalan", + path: "/dashboard/land", + icon: , + }, + { + title: "SPBU & Charger", + path: "/dashboard/gas-station", + icon: , + }, + ]; + + const mapItems = allMapItems + .filter((item) => !allowedMapPaths || allowedMapPaths.includes(item.path)) + .map((item) => ({ ...item, isActive: checkIsActive(activePath, item.path) })); + + return [ + { + label: "Ikhtisar", + items: [ + { + title: "Beranda", + path: "/dashboard", + icon: , + isActive: checkIsActive(activePath, "/dashboard"), + }, + ], + }, + { + label: "Peta Visual", + items: mapItems, + }, + { + label: "Administrasi", + items: [ + { + title: "Pengaturan", + path: "/dashboard/settings", + icon: , + isActive: checkIsActive(activePath, "/dashboard/settings"), + }, + ], + }, + ]; +}; + +export const navGroups = getNavGroups(); + +export const footerNavLinks: SidebarNavItem[] = [ + { + title: "Pusat Bantuan", + path: "#/help", + icon: , + }, + { + title: "Dokumentasi", + path: "#/documentation", + icon: , + }, +]; + +export const getNavLinks = (activePath?: string, role?: string): SidebarNavItem[] => [ + ...getNavGroups(activePath, role).flatMap((group) => + group.items.flatMap((item) => + item.subItems?.length ? [item, ...item.subItems] : [item] + ) + ), + ...footerNavLinks, +]; + +export const navLinks = getNavLinks(); + diff --git a/frontend/components/app-shell.tsx b/frontend/components/app-shell.tsx new file mode 100644 index 0000000..8b850f0 --- /dev/null +++ b/frontend/components/app-shell.tsx @@ -0,0 +1,39 @@ +import { cn } from "@/lib/utils"; +import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"; +import { AppHeader } from "@/components/app-header"; +import { AppSidebar } from "@/components/app-sidebar"; + +export function AppShell({ + children, + activePath, + fullBleed, + onExitFullBleed, + hideHeader = false +}: { + children: React.ReactNode; + activePath?: string; + fullBleed?: boolean; + onExitFullBleed?: () => void; + hideHeader?: boolean; +}) { + return ( + + + + {!hideHeader && ( + + )} +
+ {children} +
+
+
+ ); +} diff --git a/frontend/components/app-sidebar.tsx b/frontend/components/app-sidebar.tsx new file mode 100644 index 0000000..c7ec9bf --- /dev/null +++ b/frontend/components/app-sidebar.tsx @@ -0,0 +1,88 @@ +"use client"; + +import { cn } from "@/lib/utils"; + +import { + Sidebar, + SidebarContent, + SidebarFooter, + SidebarHeader, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "@/components/ui/sidebar"; +import { footerNavLinks, getNavGroups } from "@/components/app-shared"; +import { NavGroup } from "@/components/nav-group"; +import { useAuth } from "@/lib/auth-context"; + +export function AppSidebar({ activePath }: { activePath?: string }) { + const { user } = useAuth(); + const groups = getNavGroups(activePath, user?.role); + + return ( + span]:text-foreground/60", + "**:data-[slot=sidebar-menu-button]:[&>svg]:text-foreground/50", + "**:data-[slot=sidebar-menu-button]:[&>span]:transition-colors", + "**:data-[slot=sidebar-menu-button]:[&>svg]:transition-colors", + + // Hover states for sidebar buttons + "**:data-[slot=sidebar-menu-button]:hover:bg-sidebar-accent/50", + "**:data-[slot=sidebar-menu-button]:hover:[&>span]:text-foreground", + "**:data-[slot=sidebar-menu-button]:hover:[&>svg]:text-foreground", + + // Active state (Vermilion Pulse theme color) + "**:data-[slot=sidebar-menu-button]:data-[active]:bg-primary/10!", + "**:data-[slot=sidebar-menu-button]:data-[active]:[&>span]:text-primary!", + "**:data-[slot=sidebar-menu-button]:data-[active]:[&>span]:font-semibold", + "**:data-[slot=sidebar-menu-button]:data-[active]:[&>svg]:text-primary!", + "**:data-[slot=sidebar-menu-button]:data-[active]:hover:bg-primary/15!", + + // Default text/icon colors for sidebar sub-buttons (faded) + "**:data-[slot=sidebar-menu-sub-button]:[&>span]:text-foreground/60", + "**:data-[slot=sidebar-menu-sub-button]:[&>span]:transition-colors", + + // Hover states for sub-buttons + "**:data-[slot=sidebar-menu-sub-button]:hover:bg-sidebar-accent/50", + "**:data-[slot=sidebar-menu-sub-button]:hover:[&>span]:text-foreground", + + // Active state for sub-buttons + "**:data-[slot=sidebar-menu-sub-button]:data-[active]:bg-primary/10!", + "**:data-[slot=sidebar-menu-sub-button]:data-[active]:[&>span]:text-primary!", + "**:data-[slot=sidebar-menu-sub-button]:data-[active]:[&>span]:font-semibold", + "**:data-[slot=sidebar-menu-sub-button]:data-[active]:hover:bg-primary/15!" + )} + collapsible="icon" + variant="sidebar" + > + + {/* eslint-disable-next-line @next/next/no-img-element */} + }>Waras LogoWaras + + + {groups.map((group, index) => ( + + ))} + + + + {footerNavLinks.map((item) => ( + + }>{item.icon}{item.title} + + ))} + +
+

+ © {new Date().getFullYear()} Waras +

+
+
+
+ ); +} diff --git a/frontend/components/auth-divider.tsx b/frontend/components/auth-divider.tsx new file mode 100644 index 0000000..bdd8899 --- /dev/null +++ b/frontend/components/auth-divider.tsx @@ -0,0 +1,16 @@ +import type React from "react"; + +export function AuthDivider({ + children, + ...props +}: React.ComponentProps<"div">) { + return ( +
+
+
+ {children} +
+
+
+ ); +} diff --git a/frontend/components/auth-page.tsx b/frontend/components/auth-page.tsx new file mode 100644 index 0000000..4774225 --- /dev/null +++ b/frontend/components/auth-page.tsx @@ -0,0 +1,251 @@ +"use client"; + +import { useState } from "react"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import { useAuth } from "@/lib/auth-context"; +import { Logo } from "@/components/logo"; +import { Button } from "@/components/ui/button"; +import { + InputGroup, + InputGroupAddon, + InputGroupButton, + InputGroupInput, +} from "@/components/ui/input-group"; +import { FloatingPaths } from "@/components/floating-paths"; +import { + ArrowLeftIcon, + AtSignIcon, + EyeIcon, + EyeOffIcon, + KeyRoundIcon, +} from "@/components/ui/phosphor-icons"; + +const BACKEND_URL = + process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:4000"; + +const ROLE_HOME: Record = { + superadmin: "/dashboard", + admin_poverty: "/dashboard/poverty", + admin_lands_roads: "/dashboard/land", + admin_gas_stations: "/dashboard/gas-station", +}; + +const DEMO_CREDENTIALS = [ + { + label: "Super Admin", + username: "superadmin", + password: "Super@1234", + routes: "All routes", + }, + { + label: "Admin Poverty Map", + username: "admin_poverty", + password: "Poverty@1234", + routes: "/dashboard/poverty only", + }, + { + label: "Admin Lahan & Jalan", + username: "admin_lands", + password: "Lands@1234", + routes: "/dashboard/land only", + }, + { + label: "Admin SPBU", + username: "admin_gas", + password: "Gas@1234", + routes: "/dashboard/gas-station only", + }, +]; + +export function AuthPage() { + const { login } = useAuth(); + const router = useRouter(); + const [identifier, setIdentifier] = useState(""); + const [password, setPassword] = useState(""); + const [error, setError] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const [showPassword, setShowPassword] = useState(false); + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + setError(null); + setIsLoading(true); + + try { + const res = await fetch(`${BACKEND_URL}/auth/login`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + username: identifier.trim(), + password, + }), + }); + + const data = await res.json(); + + if (!res.ok) { + setError(data.error ?? "Login failed"); + return; + } + + login(data.token, data.user); + + const params = new URLSearchParams(window.location.search); + const redirectTo = params.get("redirectTo"); + + router.replace( + redirectTo ?? ROLE_HOME[data.user.role] ?? "/dashboard", + ); + } catch { + setError("Cannot connect to server. Please try again."); + } finally { + setIsLoading(false); + } + } + + return ( +
+
+
+ + + +
+
+

+ “Aplikasi ini berguna dalam membantu mengelola data spasial + dengan lebih efisien.” +

+ +
+ ~ Pangkywara +
+
+
+ +
+ + +
+
+ +
+ + + Kembali + + + {/* Top Shades */} +
+
+
+
+
+ +
+ + +
+

Login

+ +

+ Masuk ke akun Anda untuk mengakses dashboard. +

+
+ +
+ + setIdentifier(e.target.value)} + autoComplete="username" + required + /> + + + + + + + + setPassword(e.target.value)} + autoComplete="current-password" + required + /> + + + + + + + setShowPassword((value) => !value)} + > + {showPassword ? : } + + + + + {error && ( +

+ {error} +

+ )} + + +
+ + {/* Quick Demo Credentials */} +
+
+

Demo Credentials (Quick Login)

+

Click a role to auto-fill the login form:

+
+
+ {DEMO_CREDENTIALS.map((cred) => ( + + ))} +
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/components/custom-sidebar-trigger.tsx b/frontend/components/custom-sidebar-trigger.tsx new file mode 100644 index 0000000..671e268 --- /dev/null +++ b/frontend/components/custom-sidebar-trigger.tsx @@ -0,0 +1,22 @@ +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { Kbd, KbdGroup } from "@/components/ui/kbd"; +import { SidebarTrigger } from "@/components/ui/sidebar"; + +export function CustomSidebarTrigger() { + return ( + + } /> + + Toggle Sidebar{" "} + + + b + + + + ); +} diff --git a/frontend/components/dashboard-activity.tsx b/frontend/components/dashboard-activity.tsx new file mode 100644 index 0000000..ae0bbd8 --- /dev/null +++ b/frontend/components/dashboard-activity.tsx @@ -0,0 +1,80 @@ +import { + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { DashboardCard } from "@/components/dashboard-card"; +import { + FuelIcon, + LayersIcon, + UserPlusIcon, +} from "@/components/ui/phosphor-icons"; +import type { ActivityItem, ActivityKind } from "@/lib/dashboard-types"; + +interface DashboardActivityProps { + items: ActivityItem[]; +} + +const KIND_ICONS: Record = { + household: , + land: , + gas: , +}; + +function timeAgo(iso: string): string { + const then = new Date(iso).getTime(); + if (Number.isNaN(then)) return ""; + const diffMs = Date.now() - then; + const minutes = Math.floor(diffMs / 60_000); + if (minutes < 1) return "Baru saja"; + if (minutes < 60) return `${minutes} menit lalu`; + const hours = Math.floor(minutes / 60); + if (hours < 24) return `${hours} jam lalu`; + const days = Math.floor(hours / 24); + if (days < 30) return `${days} hari lalu`; + return new Date(iso).toLocaleDateString("id-ID", { + day: "numeric", + month: "short", + year: "numeric", + }); +} + +export function DashboardActivity({ items }: DashboardActivityProps) { + return ( + + + Aktivitas + Pembaruan terbaru di basis data. + + + {items.length === 0 ? ( +
+ Belum ada aktivitas. +
+ ) : ( +
    + {items.map((item) => ( +
  • + +
    +

    + {item.title} +

    +

    + {timeAgo(item.created_at)} +

    +
    +
  • + ))} +
+ )} +
+
+ ); +} diff --git a/frontend/components/dashboard-card.tsx b/frontend/components/dashboard-card.tsx new file mode 100644 index 0000000..e87e7d1 --- /dev/null +++ b/frontend/components/dashboard-card.tsx @@ -0,0 +1,15 @@ +import { cn } from "@/lib/utils"; +import type * as React from "react"; +import { Card } from "@/components/ui/card"; + +export function DashboardCard({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} diff --git a/frontend/components/dashboard-skeleton.tsx b/frontend/components/dashboard-skeleton.tsx new file mode 100644 index 0000000..1ebfa80 --- /dev/null +++ b/frontend/components/dashboard-skeleton.tsx @@ -0,0 +1,20 @@ +import { cn } from "@/lib/utils"; + +export function DashboardSkeleton() { + return ( +
+
+
+
+
+
+
+
+
+ ); +} diff --git a/frontend/components/dashboard.tsx b/frontend/components/dashboard.tsx new file mode 100644 index 0000000..87d7f60 --- /dev/null +++ b/frontend/components/dashboard.tsx @@ -0,0 +1,109 @@ +"use client"; + +import { useEffect, useMemo, useState } from "react"; +import { DashboardActivity } from "@/components/dashboard-activity"; +import { DashboardSkeleton } from "@/components/dashboard-skeleton"; +import { HouseholdsTrendChart } from "@/components/households-trend-chart"; +import { PriorityBreakdown } from "@/components/priority-breakdown"; +import { PriorityScoreChart } from "@/components/priority-score-chart"; +import { RecentHouseholds } from "@/components/recent-households"; +import { DashboardStats } from "@/components/stats"; +import type { ActivityItem, GasStationStats } from "@/lib/dashboard-types"; +import type { HouseholdRow, PovertyOverview } from "@/lib/poverty-types"; + +const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL ?? "http://localhost:4000"; + +type NamedRow = { id: number; name: string; created_at?: string }; + +export function Dashboard() { + const [loading, setLoading] = useState(true); + const [overview, setOverview] = useState(null); + const [households, setHouseholds] = useState([]); + const [gasStats, setGasStats] = useState(null); + const [landMarkers, setLandMarkers] = useState([]); + const [landShapes, setLandShapes] = useState([]); + const [gasMarkers, setGasMarkers] = useState([]); + + // Aggregates everything the overview widgets need, straight from the DB. + useEffect(() => { + const token = localStorage.getItem("auth_token"); + const headers: Record = token ? { Authorization: `Bearer ${token}` } : {}; + const get = (path: string) => fetch(`${BACKEND_URL}${path}`, { headers }).then((r) => r.json()); + const asRows = (r: PromiseSettledResult) => + r.status === "fulfilled" && Array.isArray(r.value) ? (r.value as NamedRow[]) : []; + + Promise.allSettled([ + get("/households/stats-overview"), + get("/households"), + get("/gas-station/stats"), + get("/land/markers"), + get("/land/shapes"), + get("/gas-station/markers"), + ]).then(([overviewR, hhR, gasStatsR, landMarkersR, landShapesR, gasMarkersR]) => { + if (overviewR.status === "fulfilled" && overviewR.value?.totals) { + setOverview(overviewR.value as PovertyOverview); + } + if (hhR.status === "fulfilled" && Array.isArray(hhR.value)) { + setHouseholds(hhR.value as HouseholdRow[]); + } + if (gasStatsR.status === "fulfilled" && typeof gasStatsR.value?.total === "number") { + setGasStats(gasStatsR.value as GasStationStats); + } + setLandMarkers(asRows(landMarkersR)); + setLandShapes(asRows(landShapesR)); + setGasMarkers(asRows(gasMarkersR)); + setLoading(false); + }); + }, []); + + const activity = useMemo(() => { + const items: ActivityItem[] = [ + ...households.map((h) => ({ + id: `hh-${h.id}`, + kind: "household" as const, + title: `Warga "${h.head_name}" terdata`, + created_at: h.created_at, + })), + ...landMarkers.map((m) => ({ + id: `lm-${m.id}`, + kind: "land" as const, + title: `Penanda lahan "${m.name}" ditambahkan`, + created_at: m.created_at ?? "", + })), + ...landShapes.map((s) => ({ + id: `ls-${s.id}`, + kind: "land" as const, + title: `Bidang lahan "${s.name}" digambar`, + created_at: s.created_at ?? "", + })), + ...gasMarkers.map((g) => ({ + id: `gs-${g.id}`, + kind: "gas" as const, + title: `Fasilitas "${g.name}" dipetakan`, + created_at: g.created_at ?? "", + })), + ]; + return items + .filter((i) => i.created_at) + .sort((a, b) => b.created_at.localeCompare(a.created_at)) + .slice(0, 4); + }, [households, landMarkers, landShapes, gasMarkers]); + + if (loading) return ; + + return ( +
+ + + + + + +
+ ); +} diff --git a/frontend/components/decor-icon.tsx b/frontend/components/decor-icon.tsx new file mode 100644 index 0000000..551281a --- /dev/null +++ b/frontend/components/decor-icon.tsx @@ -0,0 +1,45 @@ +import { cn } from "@/lib/utils"; +import { cva, type VariantProps } from "class-variance-authority"; + +const DecorIconVariants = cva( + "pointer-events-none absolute z-1 size-5 shrink-0 stroke-1 stroke-muted-foreground", + { + variants: { + position: { + "top-left": + "top-0 left-0 -translate-x-[calc(50%+0.5px)] -translate-y-[calc(50%+0.5px)]", + "top-right": + "top-0 right-0 translate-x-[calc(50%+0.5px)] -translate-y-[calc(50%+0.5px)]", + "bottom-right": + "right-0 bottom-0 translate-x-[calc(50%+0.5px)] translate-y-[calc(50%+0.5px)]", + "bottom-left": + "bottom-0 left-0 -translate-x-[calc(50%+0.5px)] translate-y-[calc(50%+0.5px)]", + }, + }, + defaultVariants: { + position: "top-left", + }, + } +); + +type DecorIconProps = React.ComponentProps<"svg"> & + VariantProps; + +export function DecorIcon({ position, className, ...props }: DecorIconProps) { + return ( + + ); +} diff --git a/frontend/components/delta.tsx b/frontend/components/delta.tsx new file mode 100644 index 0000000..1197fac --- /dev/null +++ b/frontend/components/delta.tsx @@ -0,0 +1,179 @@ +"use client"; +import { cn } from "@/lib/utils"; +import * as React from "react"; +import { Badge } from "@/components/ui/badge"; +import { MinusIcon, TrendingUpIcon, ArrowUpIcon, ChevronUpIcon, TrendingDownIcon, ArrowDownIcon, ChevronDownIcon } from "@/components/ui/phosphor-icons"; + +type DeltaIconVariant = "default" | "trend" | "arrow"; +type DeltaVariant = "default" | "badge"; + +type DeltaContextValue = { + value: number; +}; + +const DeltaContext = React.createContext(null); + +function useDeltaValue() { + const context = React.useContext(DeltaContext); + + if (!context) { + throw new Error( + "DeltaIcon and DeltaValue must be used inside a `Delta` component." + ); + } + + return context.value; +} + +function Delta({ + className, + value, + variant = "default", + ...props +}: React.ComponentProps<"div"> & { + value: number; + variant?: DeltaVariant; +}) { + return ( + + {variant === "badge" ? ( + 0 + ? "bg-emerald-500/10 text-emerald-500" + : "bg-red-500/10 text-red-500", + className + )} + data-slot="delta" + variant="secondary" + {...(props as React.ComponentProps)} + /> + ) : ( +
0 ? "text-emerald-600 dark:text-emerald-400" : "", + value < 0 ? "text-rose-600 dark:text-rose-400" : "", + className + )} + data-slot="delta" + {...props} + /> + )} + + ); +} + +function FilledShell({ + value, + children, +}: { + value: number; + children: React.ReactNode; +}) { + return ( + 0 && "bg-emerald-500", + value < 0 && "bg-red-500", + (!value || value === 0) && "bg-muted-foreground" + )} + data-slot="delta-icon" + > + {children} + + ); +} + +function DeltaIcon({ + variant = "default", + filled = false, + className, + ...props +}: Omit, "fill"> & { + variant?: DeltaIconVariant; + filled?: boolean; +}) { + const resolvedValue = useDeltaValue(); + + const mergedClassName = cn(className); + + const shell = (node: React.ReactElement) => + filled ? {node} : node; + + const slotProps = filled ? {} : { "data-slot": "delta-icon" as const }; + + if (!resolvedValue || resolvedValue === 0) { + return shell( + + ); + } + + if (resolvedValue > 0) { + if (variant === "trend") { + return shell( + + ); + } + + if (variant === "arrow") { + return shell( + + ); + } + + return shell( + + ); + } + + if (variant === "trend") { + return shell( + + ); + } + + if (variant === "arrow") { + return shell( + + ); + } + + return shell( + + ); +} + +function DeltaValue({ + className, + precision = 1, + suffix = "%", + absolute = true, + ...props +}: React.ComponentProps<"span"> & { + precision?: number; + suffix?: string; + absolute?: boolean; +}) { + const resolvedValue = useDeltaValue(); + + const formattedValue = ( + absolute ? Math.abs(resolvedValue) : resolvedValue + ).toFixed(precision); + + return ( + + {formattedValue} + {suffix} + + ); +} +export { Delta, DeltaIcon, DeltaValue }; diff --git a/frontend/components/floating-paths.tsx b/frontend/components/floating-paths.tsx new file mode 100644 index 0000000..1288a40 --- /dev/null +++ b/frontend/components/floating-paths.tsx @@ -0,0 +1,53 @@ +"use client"; +import { motion } from "motion/react"; + +export function FloatingPaths({ position }: { position: number }) { + const paths = Array.from({ length: 36 }, (_, i) => ({ + id: i, + d: `M-${380 - i * 5 * position} -${189 + i * 6}C-${ + 380 - i * 5 * position + } -${189 + i * 6} -${312 - i * 5 * position} ${216 - i * 6} ${ + 152 - i * 5 * position + } ${343 - i * 6}C${616 - i * 5 * position} ${470 - i * 6} ${ + 684 - i * 5 * position + } ${875 - i * 6} ${684 - i * 5 * position} ${875 - i * 6}`, + color: `rgba(15,23,42,${0.1 + i * 0.03})`, + width: 0.5 + i * 0.03, + })); + + return ( +
+ + Background Paths + {paths.map((path) => ( + + ))} + +
+ ); +} diff --git a/frontend/components/formater.ts b/frontend/components/formater.ts new file mode 100644 index 0000000..eca6395 --- /dev/null +++ b/frontend/components/formater.ts @@ -0,0 +1,95 @@ +export const DASHBOARD_LOCALE = "en-US"; + +/** Noon anchor avoids off-by-one labels around timezone boundaries for ISO date strings. */ +export function parseIsoCalendarDate(isoDate: string): Date { + return new Date(`${isoDate}T12:00:00`); +} + +export type DashboardDateStyle = "month" | "day-month" | "full"; + +export function formatDate(isoDate: string, style: DashboardDateStyle): string { + const date = parseIsoCalendarDate(isoDate); + if (style === "month") { + return date.toLocaleDateString(DASHBOARD_LOCALE, { month: "short" }); + } + if (style === "day-month") { + return date.toLocaleDateString(DASHBOARD_LOCALE, { + day: "numeric", + month: "short", + }); + } + return date.toLocaleDateString(DASHBOARD_LOCALE, { + day: "numeric", + month: "short", + year: "numeric", + }); +} + +/** X-axis for range charts: weekday when showing ~a week, otherwise month + day. */ +export function formatChartAxisTick( + isoDate: string, + periodDays: number +): string { + const date = parseIsoCalendarDate(isoDate); + if (periodDays <= 7) { + return date.toLocaleDateString(DASHBOARD_LOCALE, { weekday: "short" }); + } + return formatDate(isoDate, "day-month"); +} + +export type ChartTooltipWeekdayStyle = "short" | "long"; + +/** Tooltip label for a chart point (weekday + month + day). */ +export function formatChartTooltipDate( + isoDate: string, + weekdayStyle: ChartTooltipWeekdayStyle = "short" +): string { + const date = parseIsoCalendarDate(isoDate); + return date.toLocaleDateString(DASHBOARD_LOCALE, { + weekday: weekdayStyle, + day: "numeric", + month: "short", + }); +} + +export function formatCompactCurrency( + value: number, + options?: { maximumFractionDigits?: number } +) { + const { maximumFractionDigits = 0 } = options ?? {}; + return new Intl.NumberFormat(DASHBOARD_LOCALE, { + currency: "USD", + maximumFractionDigits, + notation: "compact", + style: "currency", + }).format(value); +} + +/** Full-precision USD (e.g. average order value). */ +export function formatFullCurrency(value: number) { + return new Intl.NumberFormat(DASHBOARD_LOCALE, { + currency: "USD", + minimumFractionDigits: 2, + maximumFractionDigits: 2, + style: "currency", + }).format(value); +} + +export function formatCompactNumber(value: number) { + return new Intl.NumberFormat(DASHBOARD_LOCALE, { + maximumFractionDigits: 1, + notation: "compact", + }).format(value); +} + +/** Whole numbers with grouping (visits, sessions, counts). */ +export function formatInteger(value: number) { + return new Intl.NumberFormat(DASHBOARD_LOCALE, { + maximumFractionDigits: 0, + }).format(value); +} + +/** Percentage with fixed decimal places (e.g. conversion rate). */ +export function formatPercent(value: number, fractionDigits = 2) { + return `${value.toFixed(fractionDigits)}%`; +} diff --git a/frontend/components/gas-station/gas-station-directory.tsx b/frontend/components/gas-station/gas-station-directory.tsx new file mode 100644 index 0000000..138ca4c --- /dev/null +++ b/frontend/components/gas-station/gas-station-directory.tsx @@ -0,0 +1,151 @@ +"use client"; + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { SearchIcon, ZapIcon } from "@/components/ui/phosphor-icons"; +import { BRAND_COLORS } from "@/lib/map-data"; +import type { UserMarker } from "@/components/leaflet-map"; +import { ContinuousTabs } from "@/components/ui/continuous-tabs"; + +interface GasStationDirectoryProps { + searchQuery: string; + setSearchQuery: (query: string) => void; + brandFilter: string; + setBrandFilter: (brand: string) => void; + evOnlyFilter: boolean; + setEvOnlyFilter: (evOnly: boolean) => void; + filteredStations: UserMarker[]; + selectedStationId: string; + onSelectStation: (station: UserMarker) => void; +} + +export function GasStationDirectory({ + searchQuery, + setSearchQuery, + brandFilter, + setBrandFilter, + evOnlyFilter, + setEvOnlyFilter, + filteredStations, + selectedStationId, + onSelectStation, +}: GasStationDirectoryProps) { + return ( + + + Direktori Stasiun Aktif + Menyaring {filteredStations.length} target stasiun regional aktif dari database. + + + {/* Filters */} +
+
+ + setSearchQuery(e.target.value)} + /> +
+ +
+
+ +
+ + {/* Grid List */} + {filteredStations.length === 0 ? ( +
+ Tidak ada lokasi yang cocok. Tambah lokasi baru melalui peta stasiun. +
+ ) : ( +
+ {filteredStations.map((station) => { + const isSelected = selectedStationId === station.id; + const brandName = station.type === "gas-pump" ? station.meta?.poi_type : null; + const brandColor = BRAND_COLORS[brandName as keyof typeof BRAND_COLORS] || { bg: "#6b7280", text: "#ffffff" }; + + return ( +
onSelectStation(station)} + className={`p-4 border rounded-lg cursor-pointer flex flex-col gap-1.5 transition-all ${ + isSelected + ? "border-primary bg-primary/5 font-semibold" + : "border-border/60 hover:bg-muted" + }`} + > +
+ {station.name} + {station.type === "gas-pump" && brandName ? ( + + {brandName} + + ) : ( + + {station.type === "charging-station" ? "EV Charger" : "Bengkel"} + + )} +
+

+ Koordinat: {station.lat.toFixed(5)}, {station.lng.toFixed(5)} +

+ + {station.type === "gas-pump" && station.meta?.gas_types && station.meta.gas_types.length > 0 && ( +
+ {station.meta.gas_types.map((gt) => ( + + {gt} + + ))} +
+ )} + +
+ {station.type === "gas-pump" ? ( + Operasional: {station.meta?.notes?.replace("Jam: ", "") || "24 Jam"} + ) : station.type === "charging-station" ? ( + Tipe: {station.meta?.poi_type} · {station.meta?.notes} + ) : ( + Spesialisasi: {station.meta?.poi_type} · {station.meta?.notes} + )} +
+
+ ); + })} +
+ )} +
+
+ ); +} diff --git a/frontend/components/gas-station/gas-station-hud.tsx b/frontend/components/gas-station/gas-station-hud.tsx new file mode 100644 index 0000000..3990381 --- /dev/null +++ b/frontend/components/gas-station/gas-station-hud.tsx @@ -0,0 +1,52 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Button } from "@/components/ui/button"; + +import { Separator } from "@/components/ui/separator"; +import { ArrowLeftIcon } from "@/components/ui/phosphor-icons"; +import { ContinuousTabs } from "@/components/ui/continuous-tabs"; + +interface GasStationHudProps { + fuelPriceDisplay: "ron92" | "ron95" | "diesel"; + setFuelPriceDisplay: (price: "ron92" | "ron95" | "diesel") => void; + brandFilter: string; + setBrandFilter: (brand: string) => void; + onCloseMap: () => void; +} + +export function GasStationHud({ + fuelPriceDisplay, + setFuelPriceDisplay, + brandFilter, + setBrandFilter, + onCloseMap, +}: GasStationHudProps) { + return ( +
+
+ TAMPILAN: + setFuelPriceDisplay(id as any)} + size="sm" + /> + + +
+
+ ); +} diff --git a/frontend/components/gas-station/gas-station-inspector.tsx b/frontend/components/gas-station/gas-station-inspector.tsx new file mode 100644 index 0000000..c06fe2e --- /dev/null +++ b/frontend/components/gas-station/gas-station-inspector.tsx @@ -0,0 +1,166 @@ +"use client"; + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { MapPinIcon, ZapIcon, ClockIcon, NavigationIcon } from "@/components/ui/phosphor-icons"; +import { BRAND_COLORS } from "@/lib/map-data"; +import type { UserMarker } from "@/components/leaflet-map"; + +interface GasStationInspectorProps { + selectedStation: UserMarker | null; +} + +const BRAND_PRICES = { + Pertamina: { ron92: 13200, ron95: 14400, diesel: 14900 }, + Shell: { ron92: 13850, ron95: 14600, diesel: 15100 }, + BP: { ron92: 13400, ron95: 14500, diesel: 14800 }, + Vivo: { ron92: 13400, ron95: 14500, diesel: 14800 } +}; + +export function GasStationInspector({ selectedStation }: GasStationInspectorProps) { + if (!selectedStation) { + return ( + + +

Pilih stasiun dari direktori atau peta untuk melihat detail.

+
+ ); + } + + const isGasPump = selectedStation.type === "gas-pump"; + const isEvCharger = selectedStation.type === "charging-station"; + const isWrench = selectedStation.type === "wrench"; + + const brandName = isGasPump ? selectedStation.meta?.poi_type : null; + const brandColor = BRAND_COLORS[brandName as keyof typeof BRAND_COLORS] || { bg: "#6b7280", text: "#ffffff" }; + + // Get price index for gas stations + const prices = isGasPump + ? (BRAND_PRICES[brandName as keyof typeof BRAND_PRICES] || BRAND_PRICES.Pertamina) + : null; + + // Extract clean values from meta + const cleanNotes = selectedStation.meta?.notes ?? ""; + const cleanPoiType = selectedStation.meta?.poi_type ?? ""; + + return ( + + + + + Detail Stasiun + + Metrik stasiun langsung & detail dari database. + + +
+

{selectedStation.name}

+ + {isGasPump ? `Jaringan ${brandName}` : isEvCharger ? "Pengisi Daya EV" : "Bengkel (Repair Shop)"} + +
+ + {/* Gas Pump: Pricing Cards */} + {isGasPump && prices && ( +
+
+ Harga RON 92 + Rp {prices.ron92.toLocaleString()} / L +
+
+ Harga RON 95 + Rp {prices.ron95.toLocaleString()} / L +
+
+ Harga Diesel + Rp {prices.diesel.toLocaleString()} / L +
+
+ )} + + {/* Gas Pump: Fuel Types */} + {isGasPump && selectedStation.meta?.gas_types && selectedStation.meta.gas_types.length > 0 && ( +
+ Tipe Bahan Bakar +
+ {selectedStation.meta.gas_types.map((gt) => ( + + {gt} + + ))} +
+
+ )} + + {/* EV Charger Details */} + {isEvCharger && ( +
+
+ Tipe Charger + {cleanPoiType} +
+
+ Status Operasional + + {cleanNotes.replace("Status: ", "")} + +
+
+ )} + + {/* Repair Shop Details */} + {isWrench && ( +
+
+ Spesialisasi + {cleanPoiType} +
+
+ Status / Jam Buka + {cleanNotes} +
+
+ )} + + {/* General: Operating Hours HUD style */} + {isGasPump && ( +
+ Jam Operasional +
+ +
+

Buka Layanan

+

{cleanNotes.replace("Jam: ", "") || "24 Jam"}

+
+
+
+ )} + + {/* Coordinates & Route Buttons */} +
+

+ + Lat: {selectedStation.lat.toFixed(5)}, Lng: {selectedStation.lng.toFixed(5)} +

+ +
+
+
+ ); +} diff --git a/frontend/components/gas-station/gas-station-stats-cards.tsx b/frontend/components/gas-station/gas-station-stats-cards.tsx new file mode 100644 index 0000000..5e80724 --- /dev/null +++ b/frontend/components/gas-station/gas-station-stats-cards.tsx @@ -0,0 +1,117 @@ +import { Card } from "@/components/ui/card"; +import { FuelIcon } from "@/components/ui/phosphor-icons"; + +export interface FuelPriceData { + region: string; + pertalite: number | null; + pertamax: number | null; + pertamaxGreen: number | null; + pertamaxTurbo: number | null; + pertamaxPertashop: number | null; + pertaminaDex: number | null; + dexlite: number | null; + bioSolarNonSubsidi: number | null; + bioSolarSubsidi: number | null; +} + +export interface FuelPricesResponse { + succeeded: boolean; + lastUpdated: string; + gasoline: FuelPriceData[]; + diesel: FuelPriceData[]; +} + +interface GasStationStatsCardsProps { + fuelPrices: FuelPricesResponse | null; + loading: boolean; +} + +export function GasStationStatsCards({ fuelPrices, loading }: GasStationStatsCardsProps) { + // Find Kalimantan Barat prices + const kalbarGas = fuelPrices?.gasoline?.find( + (g) => g.region === "Prov. Kalimantan Barat" + ); + const kalbarDiesel = fuelPrices?.diesel?.find( + (d) => d.region === "Prov. Kalimantan Barat" + ); + + const formatPrice = (p: number | null | undefined) => { + if (loading) return "Loading..."; + if (p == null) return "Rp —"; + return "Rp " + p.toLocaleString("id-ID"); + }; + + const lastUpdatedText = fuelPrices?.lastUpdated || "Update terbaru"; + + return ( + <> + {/* Card 1: Pertalite */} + +
+
+ Pertalite (RON 90) + +
+
+ {formatPrice(kalbarGas?.pertalite)} +
+
+
+ Prov. Kalimantan Barat + {lastUpdatedText} +
+
+ + {/* Card 2: Pertamax */} + +
+
+ Pertamax (RON 92) + +
+
+ {formatPrice(kalbarGas?.pertamax)} +
+
+
+ Prov. Kalimantan Barat + {lastUpdatedText} +
+
+ + {/* Card 3: Dexlite */} + +
+
+ Dexlite (Diesel) + +
+
+ {formatPrice(kalbarDiesel?.dexlite)} +
+
+
+ Prov. Kalimantan Barat + {lastUpdatedText} +
+
+ + {/* Card 4: Bio Solar (Subsidi) */} + +
+
+ Bio Solar (Subsidi) + +
+
+ {formatPrice(kalbarDiesel?.bioSolarSubsidi)} +
+
+
+ Prov. Kalimantan Barat + {lastUpdatedText} +
+
+ + ); +} diff --git a/frontend/components/gas-station/pertamina-price-table.tsx b/frontend/components/gas-station/pertamina-price-table.tsx new file mode 100644 index 0000000..3450b15 --- /dev/null +++ b/frontend/components/gas-station/pertamina-price-table.tsx @@ -0,0 +1,156 @@ +"use client"; + +import { useState, useMemo } from "react"; +import { CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { DashboardCard } from "@/components/dashboard-card"; +import { Input } from "@/components/ui/input"; +import { SearchIcon, FuelIcon } from "@/components/ui/phosphor-icons"; +import { ContinuousTabs } from "@/components/ui/continuous-tabs"; +import type { FuelPricesResponse } from "./gas-station-stats-cards"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; + +interface PertaminaPriceTableProps { + fuelPrices: FuelPricesResponse | null; + loading: boolean; +} + +export function PertaminaPriceTable({ fuelPrices, loading }: PertaminaPriceTableProps) { + const [activeTab, setActiveTab] = useState("gasoline"); + const [searchQuery, setSearchQuery] = useState(""); + + const dataList = useMemo(() => { + if (!fuelPrices) return []; + return activeTab === "gasoline" ? fuelPrices.gasoline : fuelPrices.diesel; + }, [fuelPrices, activeTab]); + + const filteredData = useMemo(() => { + return dataList.filter((row) => + row.region?.toLowerCase().includes(searchQuery.toLowerCase()) + ); + }, [dataList, searchQuery]); + + const formatVal = (val: number | null) => { + if (val == null) return ; + return Rp {val.toLocaleString("id-ID")}; + }; + + return ( + + +
+ + + Daftar Harga BBM Pertamina Seluruh Indonesia + + + Data real-time disinkronkan dari Pertamina Patra Niaga ({fuelPrices?.lastUpdated || "Update terbaru"}). + +
+ +
+
+ + setSearchQuery(e.target.value)} + /> +
+ +
+
+ + + {loading ? ( +
+ Memuat data harga BBM terbaru... +
+ ) : filteredData.length === 0 ? ( +
+ Tidak ada data provinsi yang cocok. +
+ ) : ( +
+ + + + Provinsi / Wilayah + {activeTab === "gasoline" ? ( + <> + Pertalite (90) + Pertamax (92) + Pertamax Green 95 + Pertamax Turbo (98) + Pertamax Pertashop + + ) : ( + <> + Bio Solar (Subsidi) + Bio Solar (Non-Sub) + Dexlite + Pertamina Dex + + )} + + + + {filteredData.map((row) => { + const isKalbar = row.region === "Prov. Kalimantan Barat"; + return ( + + + {isKalbar && ( + + )} + {row.region} + + {activeTab === "gasoline" ? ( + <> + {formatVal(row.pertalite)} + {formatVal(row.pertamax)} + {formatVal(row.pertamaxGreen)} + {formatVal(row.pertamaxTurbo)} + {formatVal(row.pertamaxPertashop)} + + ) : ( + <> + {formatVal(row.bioSolarSubsidi)} + {formatVal(row.bioSolarNonSubsidi)} + {formatVal(row.dexlite)} + {formatVal(row.pertaminaDex)} + + )} + + ); + })} + +
+
+ )} +
+
+ ); +} diff --git a/frontend/components/gas-station/station-edit-dialog.tsx b/frontend/components/gas-station/station-edit-dialog.tsx new file mode 100644 index 0000000..125a30b --- /dev/null +++ b/frontend/components/gas-station/station-edit-dialog.tsx @@ -0,0 +1,571 @@ +"use client"; +/* eslint-disable @typescript-eslint/no-explicit-any, react-hooks/set-state-in-effect */ + + +import { useState, useEffect, useRef } from "react"; +import { toast } from "sonner"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { cn } from "@/lib/utils"; +import type { UserMarker } from "@/components/leaflet-map"; +import { TimedUndoAction } from "@/components/time-undo-action"; +import { + EV_TYPES, + EV_STATUSES, + GAS_BRANDS, + REPAIR_SPECIALITIES, + GAS_TYPES_BY_BRAND, +} from "@/components/gas-station/station-placement-dialog"; + +/* ─── Helpers ───────────────────────────────────── */ + +function Field({ label, children, className }: { label: string; children: React.ReactNode; className?: string }) { + return ( +
+ + {label} + + {children} +
+ ); +} + +function ChipSelector({ + options, + value, + onChange, +}: { + options: { value: string; label: string; color: string }[]; + value: string; + onChange: (v: string) => void; +}) { + return ( +
+ {options.map((opt) => { + const isSelected = value === opt.value; + return ( + + ); + })} +
+ ); +} + +function MultiChipSelector({ + options, + values, + onChange, + color, +}: { + options: string[]; + values: string[]; + onChange: (v: string[]) => void; + color: string; +}) { + return ( +
+ {options.map((opt) => { + const isSelected = values.includes(opt); + return ( + + ); + })} +
+ ); +} + +/* ─── Edit Dialog ───────────────────────────────── */ + +interface StationEditDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + marker: UserMarker | null; + onUpdated: (updated: UserMarker) => void; + onDeleted: (deleted: UserMarker) => void; +} + +export function StationEditDialog({ + open, + onOpenChange, + marker, + onUpdated, + onDeleted, +}: StationEditDialogProps) { + const [category, setCategory] = useState("gas-pump"); + const [name, setName] = useState(""); + + // Form fields for EV Charger + const [evType, setEvType] = useState("DC Fast"); + const [evStatus, setEvStatus] = useState("Active"); + + // Form fields for Gas Station + const [gasBrand, setGasBrand] = useState("Pertamina"); + const [gasHours, setGasHours] = useState("24 Jam"); + const [selectedGasTypes, setSelectedGasTypes] = useState([]); + + // Form fields for Repair Shop + const [repairSpeciality, setRepairSpeciality] = useState("Umum"); + const [repairStatus, setRepairStatus] = useState("Buka 08:00 - 17:00"); + + // Timed Delete State + const DELETE_SECONDS = 5; + const TOAST_ID = "station-delete"; + const [deleteActive, setDeleteActive] = useState(false); + const [deleteCountdown, setDeleteCountdown] = useState(DELETE_SECONDS); + const pendingDeleteMarker = useRef(null); + + // Fetching and saving state + const [isLoading, setIsLoading] = useState(false); + const [isSubmitting, setIsSubmitting] = useState(false); + const [isDeleting, setIsDeleting] = useState(false); + const [error, setError] = useState(null); + + const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL ?? "http://localhost:4000"; + + // Synchronize when marker opens and load latest from DB + useEffect(() => { + if (!open || !marker) return; + + setDeleteActive(false); + setDeleteCountdown(DELETE_SECONDS); + pendingDeleteMarker.current = null; + toast.dismiss(TOAST_ID); + setError(null); + + const dbId = marker.id.split("-").slice(1).join("-"); + const token = localStorage.getItem("auth_token"); + const headers: Record = token ? { Authorization: `Bearer ${token}` } : {}; + + setIsLoading(true); + + fetch(`${BACKEND_URL}/gas-station/markers/${dbId}`, { headers }) + .then((r) => { + if (!r.ok) throw new Error("Gagal memuat data dari server"); + return r.json(); + }) + .then((row) => { + setCategory(row.marker_category); + setName(row.name); + if (row.marker_category === "charging-station") { + setEvType(row.sub_type ?? "DC Fast"); + setEvStatus(row.status ?? "Active"); + } else if (row.marker_category === "gas-pump") { + setGasBrand(row.brand ?? "Pertamina"); + setGasHours(row.operating_hours ?? "24 Jam"); + setSelectedGasTypes(row.gas_types ?? GAS_TYPES_BY_BRAND[row.brand ?? "Pertamina"] ?? []); + } else if (row.marker_category === "wrench") { + setRepairSpeciality(row.sub_type ?? "Umum"); + setRepairStatus(row.status ?? "Buka 08:00 - 17:00"); + } + }) + .catch((err) => { + console.error(err); + setError("Gagal memuat data terbaru, menggunakan data lokal."); + // Fallback + setCategory(marker.type); + setName(marker.name); + const meta = marker.meta; + if (marker.type === "charging-station") { + setEvType(meta?.poi_type ?? "DC Fast"); + setEvStatus(meta?.notes?.replace("Status: ", "") ?? "Active"); + } else if (marker.type === "gas-pump") { + setGasBrand(meta?.poi_type ?? "Pertamina"); + setGasHours(meta?.notes?.replace("Jam: ", "") ?? "24 Jam"); + setSelectedGasTypes(meta?.gas_types ?? GAS_TYPES_BY_BRAND[meta?.poi_type ?? "Pertamina"] ?? []); + } else if (marker.type === "wrench") { + setRepairSpeciality(meta?.poi_type ?? "Umum"); + setRepairStatus(meta?.notes ?? "Buka 08:00 - 17:00"); + } + }) + .finally(() => setIsLoading(false)); + }, [open, marker]); + + async function handleDelete() { + const target = pendingDeleteMarker.current; + if (!target) return; + + const dbId = target.id.split("-").slice(1).join("-"); + const token = localStorage.getItem("auth_token"); + const headers: Record = token ? { Authorization: `Bearer ${token}` } : {}; + + setIsDeleting(true); + setError(null); + + try { + const res = await fetch(`${BACKEND_URL}/gas-station/markers/${dbId}`, { + method: "DELETE", + headers, + }); + + if (!res.ok && res.status !== 204) { + throw new Error("Gagal menghapus lokasi dari server"); + } + + pendingDeleteMarker.current = null; + onDeleted(target); + onOpenChange(false); + } catch (err: unknown) { + setError(err instanceof Error ? err.message : "Terjadi kesalahan saat menghapus"); + toast.error(err instanceof Error ? err.message : "Gagal menghapus lokasi"); + } finally { + setIsDeleting(false); + } + } + + // Countdown tick + useEffect(() => { + if (!deleteActive) return; + const id = setInterval(() => setDeleteCountdown((p) => Math.max(0, p - 1)), 1000); + return () => clearInterval(id); + }, [deleteActive]); + + // Fire delete when countdown hits 0 + useEffect(() => { + if (!deleteActive || deleteCountdown > 0) return; + setDeleteActive(false); + setDeleteCountdown(DELETE_SECONDS); + toast.dismiss(TOAST_ID); + handleDelete(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [deleteActive, deleteCountdown]); + + // Toast showing countdown + useEffect(() => { + if (!deleteActive || !pendingDeleteMarker.current) { + toast.dismiss(TOAST_ID); + return; + } + const mName = pendingDeleteMarker.current.name; + const pct = (deleteCountdown / DELETE_SECONDS) * 100; + toast.custom( + () => ( +
+
+
+

Menghapus lokasi

+

"{mName}" — dalam {deleteCountdown} detik

+
+ +
+ ), + { id: TOAST_ID, duration: Infinity, position: "bottom-left" }, + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [deleteActive, deleteCountdown]); + + function toggleDelete() { + setDeleteActive((prev) => { + if (!prev) { + pendingDeleteMarker.current = marker; + setDeleteCountdown(DELETE_SECONDS); + } else { + pendingDeleteMarker.current = null; + toast.dismiss(TOAST_ID); + } + return !prev; + }); + } + + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + if (!marker) return; + + setIsSubmitting(true); + setError(null); + + let finalPoiType = ""; + let finalNotes = ""; + + const brand = category === "gas-pump" ? gasBrand : null; + const sub_type = category === "charging-station" ? evType : (category === "wrench" ? repairSpeciality : null); + const gas_types = category === "gas-pump" ? selectedGasTypes : null; + const operating_hours = category === "gas-pump" ? gasHours : null; + const status = category === "charging-station" ? evStatus : (category === "wrench" ? repairStatus : null); + + if (category === "charging-station") { + finalPoiType = evType; + finalNotes = `Status: ${evStatus}`; + } else if (category === "gas-pump") { + finalPoiType = gasBrand; + finalNotes = `Jam: ${gasHours}`; + } else if (category === "wrench") { + finalPoiType = repairSpeciality; + finalNotes = repairStatus; + } + + try { + const dbId = marker.id.split("-").slice(1).join("-"); + const token = localStorage.getItem("auth_token"); + const headers = { + "Content-Type": "application/json", + ...(token ? { Authorization: `Bearer ${token}` } : {}), + }; + + const res = await fetch(`${BACKEND_URL}/gas-station/markers/${dbId}`, { + method: "PUT", + headers, + body: JSON.stringify({ + name: name.trim() || marker.name, + marker_category: category, + brand, + sub_type, + gas_types, + operating_hours, + status, + notes: null, + }), + }); + + if (!res.ok) { + const data = await res.json().catch(() => ({})); + throw new Error(data.error ?? "Gagal menyimpan perubahan"); + } + + const saved = await res.json(); + + const updated: UserMarker = { + ...marker, + type: category, + name: saved.name, + meta: { + ...marker.meta, + poi_type: finalPoiType, + notes: finalNotes, + gas_types: category === "gas-pump" ? selectedGasTypes : undefined, + }, + }; + + onUpdated(updated); + onOpenChange(false); + } catch (err: unknown) { + setError(err instanceof Error ? err.message : "Terjadi kesalahan"); + } finally { + setIsSubmitting(false); + } + } + + const categoryLabels: Record = { + "charging-station": "Edit EV Charger", + "gas-pump": "Edit Gas Station", + wrench: "Edit Bengkel", + }; + + const titleLabel = categoryLabels[category] || "Edit Lokasi"; + + return ( + { if (!v) onOpenChange(false); }}> + +
+ {/* Header */} + + {titleLabel} +

+ {marker ? `${marker.lat.toFixed(5)}, ${marker.lng.toFixed(5)}` : ""} +

+
+ + {/* Body */} +
+ {isLoading ? ( +

Memuat data…

+ ) : ( + <> + {/* Category Selector */} + + + + + {/* Location Name */} + + setName(e.target.value)} + required + className="bg-muted/50 border border-border focus-visible:border-ring" + /> + + + {/* Dynamic Fields: EV Charger */} + {category === "charging-station" && ( + <> + + + + + + + + )} + + {/* Dynamic Fields: Gas Station */} + {category === "gas-pump" && ( + <> + + { + setGasBrand(brand); + setSelectedGasTypes(GAS_TYPES_BY_BRAND[brand] ?? []); + }} + /> + + + b.value === gasBrand)?.color ?? "#ff3b1f"} + /> + + + setGasHours(e.target.value)} + required + className="bg-muted/50 border border-border focus-visible:border-ring" + /> +
+ {["24 Jam", "06:00 - 22:00", "07:00 - 21:00"].map((h) => ( + + ))} +
+
+ + )} + + {/* Dynamic Fields: Repair Shop */} + {category === "wrench" && ( + <> + + + + + setRepairStatus(e.target.value)} + required + className="bg-muted/50 border border-border focus-visible:border-ring" + /> +
+ {["Buka 24 Jam", "Buka 08:00 - 17:00", "Tutup"].map((s) => ( + + ))} +
+
+ + )} + + )} + + {error && ( +

{error}

+ )} +
+ + {/* Footer */} +
+ +
+ + +
+
+
+
+
+ ); +} diff --git a/frontend/components/gas-station/station-placement-dialog.tsx b/frontend/components/gas-station/station-placement-dialog.tsx new file mode 100644 index 0000000..2a769e5 --- /dev/null +++ b/frontend/components/gas-station/station-placement-dialog.tsx @@ -0,0 +1,449 @@ +"use client"; +/* eslint-disable @typescript-eslint/no-explicit-any, react-hooks/set-state-in-effect */ + + +import { useState, useEffect } from "react"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { cn } from "@/lib/utils"; +import type { UserMarker } from "@/components/leaflet-map"; + +/* ─── Selectors Options ────────────────────────── */ + +const EV_TYPES = [ + { value: "DC Fast", label: "DC Fast", color: "#10b981" }, + { value: "Supercharger", label: "Supercharger", color: "#14b8a6" }, + { value: "AC Level 2", label: "AC Level 2", color: "#3b82f6" }, +]; + +const EV_STATUSES = [ + { value: "Active", label: "Active", color: "#10b981" }, + { value: "Maintenance", label: "Maintenance", color: "#eab308" }, + { value: "Offline", label: "Offline", color: "#ef4444" }, +]; + +const GAS_BRANDS = [ + { value: "Pertamina", label: "Pertamina", color: "#ef4444" }, + { value: "Shell", label: "Shell", color: "#eab308" }, + { value: "BP", label: "BP", color: "#10b981" }, + { value: "Vivo", label: "Vivo", color: "#3b82f6" }, +]; + +const GAS_TYPES_BY_BRAND: Record = { + Pertamina: ["Pertalite", "Pertamax", "Pertamax Green", "Pertamax Turbo", "Dexlite", "Pertamina Dex", "Solar"], + Shell: ["Shell Super", "Shell V-Power", "Shell V-Power Nitro+", "Shell V-Power Diesel"], + BP: ["BP 92", "BP 95", "BP Ultimate", "BP Ultimate Diesel"], + Vivo: ["Revvo 90", "Revvo 92", "Revvo 95"], +}; + +const REPAIR_SPECIALITIES = [ + { value: "Umum", label: "Umum", color: "#6b7280" }, + { value: "AC & Kelistrikan",label: "AC & Kelistrikan",color: "#0ea5e9" }, + { value: "Mesin", label: "Mesin", color: "#f97316" }, + { value: "Ban & Velg", label: "Ban & Velg", color: "#f59e0b" }, +]; + +/* ─── Helpers ───────────────────────────────────── */ + +function Field({ label, children, className }: { label: string; children: React.ReactNode; className?: string }) { + return ( +
+ + {label} + + {children} +
+ ); +} + +function ChipSelector({ + options, + value, + onChange, +}: { + options: { value: string; label: string; color: string }[]; + value: string; + onChange: (v: string) => void; +}) { + return ( +
+ {options.map((opt) => { + const isSelected = value === opt.value; + return ( + + ); + })} +
+ ); +} + +function MultiChipSelector({ + options, + values, + onChange, + color, +}: { + options: string[]; + values: string[]; + onChange: (v: string[]) => void; + color: string; +}) { + return ( +
+ {options.map((opt) => { + const isSelected = values.includes(opt); + return ( + + ); + })} +
+ ); +} + +/* ─── Placement Dialog ─────────────────────────── */ + +interface StationPlacementDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + pendingClick: { lat: number; lng: number } | null; + markerType: string | null; + onConfirm: (marker: UserMarker) => void; +} + +export function StationPlacementDialog({ + open, + onOpenChange, + pendingClick, + markerType, + onConfirm, +}: StationPlacementDialogProps) { + const [category, setCategory] = useState("gas-pump"); + const [name, setName] = useState(""); + + // Form fields for EV Charger + const [evType, setEvType] = useState("DC Fast"); + const [evStatus, setEvStatus] = useState("Active"); + + // Form fields for Gas Station + const [gasBrand, setGasBrand] = useState("Pertamina"); + const [gasHours, setGasHours] = useState("24 Jam"); + const [selectedGasTypes, setSelectedGasTypes] = useState([]); + + // Form fields for Repair Shop + const [repairSpeciality, setRepairSpeciality] = useState("Umum"); + const [repairStatus, setRepairStatus] = useState("Buka 08:00 - 17:00"); + + // Submission state + const [isSubmitting, setIsSubmitting] = useState(false); + const [error, setError] = useState(null); + + const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL ?? "http://localhost:4000"; + + // Synchronize with initial markerType when dialog opens + useEffect(() => { + if (open && markerType) { + setCategory(markerType); + setName(""); + setEvType("DC Fast"); + setEvStatus("Active"); + setGasBrand("Pertamina"); + setGasHours("24 Jam"); + setSelectedGasTypes(GAS_TYPES_BY_BRAND["Pertamina"]); + setRepairSpeciality("Umum"); + setRepairStatus("Buka 08:00 - 17:00"); + setError(null); + setIsSubmitting(false); + } + }, [open, markerType]); + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + if (!pendingClick) return; + + setIsSubmitting(true); + setError(null); + + let finalPoiType = ""; + let finalNotes = ""; + + const brand = category === "gas-pump" ? gasBrand : null; + const sub_type = category === "charging-station" ? evType : (category === "wrench" ? repairSpeciality : null); + const gas_types = category === "gas-pump" ? selectedGasTypes : null; + const operating_hours = category === "gas-pump" ? gasHours : null; + const status = category === "charging-station" ? evStatus : (category === "wrench" ? repairStatus : null); + + if (category === "charging-station") { + finalPoiType = evType; + finalNotes = `Status: ${evStatus}`; + } else if (category === "gas-pump") { + finalPoiType = gasBrand; + finalNotes = `Jam: ${gasHours}`; + } else if (category === "wrench") { + finalPoiType = repairSpeciality; + finalNotes = repairStatus; + } + + try { + const token = localStorage.getItem("auth_token"); + const res = await fetch(`${BACKEND_URL}/gas-station/markers`, { + method: "POST", + headers: { + "Content-Type": "application/json", + ...(token ? { Authorization: `Bearer ${token}` } : {}), + }, + body: JSON.stringify({ + name: name.trim() || `New ${category.replace("-", " ")}`, + marker_category: category, + brand, + sub_type, + gas_types, + operating_hours, + status, + latitude: pendingClick.lat, + longitude: pendingClick.lng, + notes: null, + }), + }); + + if (!res.ok) { + const data = await res.json().catch(() => ({})); + throw new Error(data.error ?? "Gagal menyimpan data"); + } + + const saved = await res.json(); + + const savedMarker: UserMarker = { + id: `gsm-${saved.id}`, + lat: pendingClick.lat, + lng: pendingClick.lng, + type: category, + name: saved.name, + meta: { + poi_type: finalPoiType, + notes: finalNotes, + gas_types: category === "gas-pump" ? selectedGasTypes : undefined, + }, + }; + + onConfirm(savedMarker); + handleCancel(); + } catch (err: unknown) { + setError(err instanceof Error ? err.message : "Terjadi kesalahan"); + } finally { + setIsSubmitting(false); + } + } + + function handleCancel() { + setName(""); + setError(null); + setIsSubmitting(false); + onOpenChange(false); + } + + const categoryLabels: Record = { + "charging-station": "EV Charger Baru", + "gas-pump": "Gas Station Baru", + wrench: "Bengkel Baru", + }; + + const titleLabel = categoryLabels[category] || "Lokasi Baru"; + + return ( + { if (!v) handleCancel(); }}> + +
+ {/* Header */} + + {titleLabel} +

+ {pendingClick ? `${pendingClick.lat.toFixed(5)}, ${pendingClick.lng.toFixed(5)}` : ""} +

+
+ + {/* Body */} +
+ {/* Category Selector */} + + + + + {/* Location Name */} + + setName(e.target.value)} + placeholder={ + category === "charging-station" + ? "Contoh: VoltCharge EV Station" + : category === "gas-pump" + ? "Contoh: EcoFuel Station Menteng" + : "Contoh: Bengkel Motor Jaya" + } + required + autoFocus + className="bg-muted/50 border border-border focus-visible:border-ring" + /> + + + {/* Dynamic Fields: EV Charger */} + {category === "charging-station" && ( + <> + + + + + + + + )} + + {/* Dynamic Fields: Gas Station */} + {category === "gas-pump" && ( + <> + + { + setGasBrand(brand); + setSelectedGasTypes(GAS_TYPES_BY_BRAND[brand] ?? []); + }} + /> + + + b.value === gasBrand)?.color ?? "#ff3b1f"} + /> + + + setGasHours(e.target.value)} + placeholder="Contoh: 24 Jam atau 06:00 - 22:00" + required + className="bg-muted/50 border border-border focus-visible:border-ring" + /> +
+ {["24 Jam", "06:00 - 22:00", "07:00 - 21:00"].map((h) => ( + + ))} +
+
+ + )} + + {/* Dynamic Fields: Repair Shop */} + {category === "wrench" && ( + <> + + + + + setRepairStatus(e.target.value)} + placeholder="Contoh: Buka 08:00 - 17:00 atau Tutup" + required + className="bg-muted/50 border border-border focus-visible:border-ring" + /> +
+ {["Buka 24 Jam", "Buka 08:00 - 17:00", "Tutup"].map((s) => ( + + ))} +
+
+ + )} + + {error && ( +

{error}

+ )} +
+ + {/* Footer */} +
+ + +
+
+
+
+ ); +} +export { EV_TYPES, EV_STATUSES, GAS_BRANDS, REPAIR_SPECIALITIES, GAS_TYPES_BY_BRAND }; diff --git a/frontend/components/header.tsx b/frontend/components/header.tsx new file mode 100644 index 0000000..05153e7 --- /dev/null +++ b/frontend/components/header.tsx @@ -0,0 +1,65 @@ +"use client"; +import { cn } from "@/lib/utils"; +import { Logo } from "@/components/logo"; +import { useScroll } from "@/hooks/use-scroll"; +import { Button } from "@/components/ui/button"; +import { MobileNav } from "@/components/mobile-nav"; + +export const navLinks = [ + { + label: "Features", + href: "#", + }, + { + label: "Pricing", + href: "#", + }, + { + label: "About", + href: "#", + }, +]; + +export function Header() { + const scrolled = useScroll(10); + + return ( +
+ +
+ ); +} diff --git a/frontend/components/households-trend-chart.tsx b/frontend/components/households-trend-chart.tsx new file mode 100644 index 0000000..cfc6223 --- /dev/null +++ b/frontend/components/households-trend-chart.tsx @@ -0,0 +1,128 @@ +"use client"; + +import type * as React from "react"; +import { Bar, BarChart, XAxis } from "recharts"; +import { + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + type ChartConfig, + ChartContainer, + ChartTooltip, + ChartTooltipContent, +} from "@/components/ui/chart"; +import { Delta, DeltaIcon, DeltaValue } from "@/components/delta"; +import { DashboardCard } from "@/components/dashboard-card"; +import { formatDate } from "@/components/formater"; +import type { PovertyOverview } from "@/lib/poverty-types"; + +interface HouseholdsTrendChartProps { + monthly: PovertyOverview["monthly"]; +} + +const chartConfig = { + count: { + label: "KK Terdata", + color: "var(--chart-2)", + }, +} satisfies ChartConfig; + +function CustomGradientBar( + props: React.SVGProps & { + index?: number; + dataKey?: string | number; + } +) { + const { + fill, + x = 0, + y = 0, + width = 0, + height = 0, + dataKey = "count", + index = 0, + } = props; + const gid = `gradient-bar-${String(dataKey)}-${index}`; + + return ( + <> + + + + + + + + + + ); +} + +export function HouseholdsTrendChart({ monthly }: HouseholdsTrendChartProps) { + const chartRows = monthly.map((m) => ({ month: m.month, count: m.count })); + + const first = chartRows[0]?.count ?? 0; + const last = chartRows.at(-1)?.count ?? 0; + const growthPct = first > 0 ? ((last - first) / first) * 100 : 0; + + return ( + + +
+ Pendataan Warga + {chartRows.length > 1 && ( + + + + + )} +
+ + KK baru terdata per bulan, 12 bulan terakhir. + +
+ + {chartRows.length === 0 ? ( +
+ Belum ada data pendataan warga. +
+ ) : ( + + + formatDate(`${value}-01`, "month")} + tickLine={false} + tickMargin={10} + /> + } + cursor={false} + /> + } + /> + + + )} +
+
+ ); +} diff --git a/frontend/components/icons/apple-icon.tsx b/frontend/components/icons/apple-icon.tsx new file mode 100644 index 0000000..37e450b --- /dev/null +++ b/frontend/components/icons/apple-icon.tsx @@ -0,0 +1,18 @@ +export function AppleIcon({ + fill = "currentColor", + ...props +}: React.ComponentProps<"svg">) { + return ( + + + + + + + ); +} diff --git a/frontend/components/icons/github-icon.tsx b/frontend/components/icons/github-icon.tsx new file mode 100644 index 0000000..ab544c8 --- /dev/null +++ b/frontend/components/icons/github-icon.tsx @@ -0,0 +1,10 @@ +export function GithubIcon(props: React.ComponentProps<"svg">) { + return ( + + + + ); +} diff --git a/frontend/components/icons/google-icon.tsx b/frontend/components/icons/google-icon.tsx new file mode 100644 index 0000000..c12a0e9 --- /dev/null +++ b/frontend/components/icons/google-icon.tsx @@ -0,0 +1,14 @@ +export function GoogleIcon(props: React.ComponentProps<"svg">) { + return ( + + + + + + ); +} diff --git a/frontend/components/land/land-allocation-chart.tsx b/frontend/components/land/land-allocation-chart.tsx new file mode 100644 index 0000000..ded12dd --- /dev/null +++ b/frontend/components/land/land-allocation-chart.tsx @@ -0,0 +1,79 @@ +/* eslint-disable react-hooks/set-state-in-effect */ +import { useState, useEffect } from "react"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip } from "recharts"; + +interface LandAllocationChartProps { + pieChartData: Array<{ + name: string; + value: number; + color: string; + }>; +} + +export function LandAllocationChart({ pieChartData }: LandAllocationChartProps) { + const [isMounted, setIsMounted] = useState(false); + useEffect(() => { + setIsMounted(true); + }, []); + + return ( + + + Land Status Breakdown + Acreage and share representation of registered parcels by ownership status. + + + {pieChartData.length > 0 ? ( + <> +
+ {!isMounted ? ( +
+ ) : ( + + + + {pieChartData.map((entry, index) => ( + + ))} + + + + + )} +
+
+ {pieChartData.map((d, index) => ( +
+
+
+ {d.name} + {d.value} Hectares +
+
+ ))} +
+ + ) : ( +

Belum ada data poligon lahan.

+ )} + + + ); +} diff --git a/frontend/components/land/land-directory.tsx b/frontend/components/land/land-directory.tsx new file mode 100644 index 0000000..352cd0d --- /dev/null +++ b/frontend/components/land/land-directory.tsx @@ -0,0 +1,96 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { SearchIcon } from "@/components/ui/phosphor-icons"; +import { LAND_MARKER_LABELS, LAND_MARKER_COLORS } from "@/lib/map-data"; +import type { UserMarker } from "@/components/leaflet-map"; +import { calcLineLength, calcPolygonArea } from "@/components/land/shape-placement-dialog"; + +interface LandDirectoryProps { + searchQuery: string; + setSearchQuery: (query: string) => void; + filteredItems: UserMarker[]; + selectedItemId: string | null; + visibilityFilters: Record; + onSelectItem: (item: UserMarker) => void; +} + +function itemBadge(item: UserMarker): string { + const coordinates = item.meta?.coordinates ?? []; + if (item.type === "polygon" && coordinates.length > 0) { + return `${(calcPolygonArea(coordinates) / 10000).toFixed(2)} Ha`; + } + if (item.type === "line" && coordinates.length > 0) { + const length = calcLineLength(coordinates); + return length >= 1000 ? `${(length / 1000).toFixed(2)} km` : `${length.toFixed(1)} m`; + } + if (item.type === "circle") { + const radius = item.meta?.radius ?? 0; + return `${radius.toFixed(1)} m`; + } + return LAND_MARKER_LABELS[item.type] ?? item.type; +} + +export function LandDirectory({ + searchQuery, + setSearchQuery, + filteredItems, + selectedItemId, + visibilityFilters, + onSelectItem, +}: LandDirectoryProps) { + return ( + + + GIS Layer Directory + Select a marker or shape to review survey metadata and inspector details. + + +
+ + setSearchQuery(e.target.value)} + /> +
+ {filteredItems.length > 0 ? ( +
+ {filteredItems.map((item) => { + const isSelected = selectedItemId === item.id; + const isVisible = visibilityFilters[item.type] !== false; + const color = LAND_MARKER_COLORS[item.type] ?? "#6b7280"; + return ( +
onSelectItem(item)} + className={`p-3 border rounded-lg cursor-pointer flex justify-between items-center gap-2 transition-all ${ + isSelected + ? "border-primary bg-primary/5 text-primary font-semibold" + : "border-border/60 hover:bg-muted" + } ${!isVisible ? "opacity-45" : ""}`} + > + {item.name} + + {itemBadge(item)} + +
+ ); + })} +
+ ) : ( +

+ Belum ada penanda atau bentuk yang ditambahkan. Buka peta untuk mulai menambahkan. +

+ )} +
+
+ ); +} diff --git a/frontend/components/land/land-inspector.tsx b/frontend/components/land/land-inspector.tsx new file mode 100644 index 0000000..5a34d79 --- /dev/null +++ b/frontend/components/land/land-inspector.tsx @@ -0,0 +1,168 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { LayersIcon, EyeIcon, EyeOffIcon, ShieldAlertIcon } from "@/components/ui/phosphor-icons"; +import { LAND_MARKER_LABELS, LAND_MARKER_COLORS } from "@/lib/map-data"; +import type { UserMarker } from "@/components/leaflet-map"; +import { calcLineLength, calcPolygonArea } from "@/components/land/shape-placement-dialog"; + +function formatDate(iso?: string): string { + if (!iso) return "—"; + return new Date(iso).toLocaleDateString("id-ID", { year: "numeric", month: "short", day: "numeric" }); +} + +interface LandInspectorTopProps { + selectedItem: UserMarker | null; + visibilityFilters: Record; + toggleFilter: (type: string) => void; +} + +export function LandInspectorTop({ selectedItem, visibilityFilters, toggleFilter }: LandInspectorTopProps) { + if (!selectedItem) { + return ( + + + + + Boundary Inspector + + Reviewing border registries. + + +

+ Belum ada item dipilih. Buka peta atau pilih item dari direktori. +

+
+
+ ); + } + + const color = LAND_MARKER_COLORS[selectedItem.type] ?? "#6b7280"; + const label = LAND_MARKER_LABELS[selectedItem.type] ?? selectedItem.type; + const coordinates = selectedItem.meta?.coordinates ?? []; + const radius = selectedItem.meta?.radius ?? 0; + const length = selectedItem.type === "line" ? calcLineLength(coordinates) : 0; + + return ( + + + + + Boundary Inspector + + Reviewing border registries. + + +
+

{selectedItem.name}

+ + {label} + +
+ +
+ {selectedItem.type === "polygon" && coordinates.length > 0 && ( +
+ Luas Terdaftar: + {(calcPolygonArea(coordinates) / 10000).toFixed(2)} Ha +
+ )} + {selectedItem.type === "line" && coordinates.length > 0 && ( +
+ Panjang: + + {length >= 1000 ? `${(length / 1000).toFixed(2)} km` : `${length.toFixed(1)} m`} + +
+ )} + {selectedItem.type === "circle" && ( +
+ Radius: + {radius.toFixed(1)} m +
+ )} + {(selectedItem.type === "line" || selectedItem.type === "polygon") && selectedItem.meta?.poi_type && ( +
+ Status: + {selectedItem.meta.poi_type} +
+ )} +
+ Created By: + {selectedItem.meta?.created_by_username ?? "—"} +
+
+ Created: + {formatDate(selectedItem.meta?.created_at)} +
+
+ +
+ Visibility filter status: +
+ +
+
+
+
+ ); +} + +interface LandInspectorBottomProps { + recentFlags: UserMarker[]; +} + +export function LandInspectorBottom({ recentFlags }: LandInspectorBottomProps) { + return ( + + + + + Surveyor Logs + + + + {recentFlags.length > 0 ? ( + recentFlags.map((flag) => ( +
+
+ {flag.name} +
+ {flag.meta?.notes && ( +

{flag.meta.notes}

+ )} + Reported: {formatDate(flag.meta?.created_at)} +
+ )) + ) : ( +
+

Belum ada tengara lahan tercatat.

+
+ )} +
+
+ ); +} diff --git a/frontend/components/land/land-stats-cards.tsx b/frontend/components/land/land-stats-cards.tsx new file mode 100644 index 0000000..f31a066 --- /dev/null +++ b/frontend/components/land/land-stats-cards.tsx @@ -0,0 +1,55 @@ +import { Card } from "@/components/ui/card"; +import { LayersIcon, RoadHorizonIcon, MapPinIcon } from "@/components/ui/phosphor-icons"; + +interface LandStatsCardsProps { + aggregateStats: { + totalParcelArea: string; + totalRoadLength: string; + activeMarkers: number; + }; +} + +export function LandStatsCards({ aggregateStats }: LandStatsCardsProps) { + return ( + <> + +
+
+ Total Parcel Area + +
+
{aggregateStats.totalParcelArea} Ha
+
+
+ Sum of all registered polygon parcels +
+
+ + +
+
+ Total Road Length + +
+
{aggregateStats.totalRoadLength} km
+
+
+ Sum of all measured road/line segments +
+
+ + +
+
+ Active Markers + +
+
{aggregateStats.activeMarkers} Markers
+
+
+ Land pins, survey flags, protected zones & registries +
+
+ + ); +} diff --git a/frontend/components/land/shape-edit-dialog.tsx b/frontend/components/land/shape-edit-dialog.tsx new file mode 100644 index 0000000..13669e6 --- /dev/null +++ b/frontend/components/land/shape-edit-dialog.tsx @@ -0,0 +1,338 @@ +"use client"; + +import { useState, useEffect, useRef } from "react"; +import { toast } from "sonner"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { cn } from "@/lib/utils"; +import type { UserMarker } from "@/components/leaflet-map"; +import { TimedUndoAction } from "@/components/time-undo-action"; +import { + ROAD_STATUSES, + LAND_STATUSES, + calcLineLength, + calcPolygonArea, +} from "@/components/land/shape-placement-dialog"; +import { LAND_MARKER_LABELS } from "@/lib/map-data"; + +/* ─── Shared Field component ─────────────────────────────── */ + +function Field({ label, children, className }: { label: string; children: React.ReactNode; className?: string }) { + return ( +
+ + {label} + + {children} +
+ ); +} + +/* ─── Stat Chip ──────────────────────────────────────────── */ + +function StatChip({ label, value }: { label: string; value: string }) { + return ( +
+ {label} + {value} +
+ ); +} + +/* ─── Status Chip Selector ───────────────────────────────── */ + +function StatusSelector({ + options, + value, + onChange, +}: { + options: { value: string; label: string; color: string }[]; + value: string; + onChange: (v: string) => void; +}) { + return ( +
+ {options.map((opt) => { + const isSelected = value === opt.value; + return ( + + ); + })} +
+ ); +} + +/* ─── Dialog Component ───────────────────────────────────── */ + +interface ShapeEditDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + shape: UserMarker | null; + onUpdated: (updated: UserMarker) => void; + onDeleted: (deleted: UserMarker) => void; +} + +export function ShapeEditDialog({ + open, + onOpenChange, + shape, + onUpdated, + onDeleted, +}: ShapeEditDialogProps) { + const [name, setName] = useState(""); + const [status, setStatus] = useState(""); + const [notes, setNotes] = useState(""); + + const DELETE_SECONDS = 5; + const TOAST_ID = "shape-delete"; + + const [deleteActive, setDeleteActive] = useState(false); + const [deleteCountdown, setDeleteCountdown] = useState(DELETE_SECONDS); + const pendingDeleteShape = useRef(null); + + const shapeType = shape?.type ?? ""; + const isLine = shapeType === "line"; + const isPolygon = shapeType === "polygon"; + const isCircle = shapeType === "circle"; + + const titleLabel = `Edit ${LAND_MARKER_LABELS[shapeType] ?? "Item"}`; + const coordinates = shape?.meta?.coordinates ?? []; + const radius = shape?.meta?.radius ?? 0; + + const length = isLine ? calcLineLength(coordinates) : 0; + const area = isPolygon + ? calcPolygonArea(coordinates) + : isCircle + ? Math.PI * radius * radius + : 0; + + const statusOptions = isLine ? ROAD_STATUSES : isPolygon ? LAND_STATUSES : []; + + // Reset when different shape opens + useEffect(() => { + if (!shape?.id) return; + setName(shape.name); + setStatus(shape.meta?.poi_type ?? (isLine ? "Regency Road" : isPolygon ? "SHM" : "")); + setNotes(shape.meta?.notes ?? ""); + setDeleteActive(false); + setDeleteCountdown(DELETE_SECONDS); + pendingDeleteShape.current = null; + toast.dismiss(TOAST_ID); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [shape?.id]); + + // Countdown tick + useEffect(() => { + if (!deleteActive) return; + const id = setInterval(() => setDeleteCountdown((p) => Math.max(0, p - 1)), 1000); + return () => clearInterval(id); + }, [deleteActive]); + + // Fire delete when countdown hits 0 + useEffect(() => { + if (!deleteActive || deleteCountdown > 0) return; + setDeleteActive(false); + setDeleteCountdown(DELETE_SECONDS); + toast.dismiss(TOAST_ID); + handleDelete(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [deleteActive, deleteCountdown]); + + // Toast while countdown is running + useEffect(() => { + if (!deleteActive || !pendingDeleteShape.current) { + toast.dismiss(TOAST_ID); + return; + } + const shapeName = pendingDeleteShape.current.name; + const pct = (deleteCountdown / DELETE_SECONDS) * 100; + toast.custom( + () => ( +
+
+
+

Menghapus shape

+

"{shapeName}" — dalam {deleteCountdown} detik

+
+ +
+ ), + { id: TOAST_ID, duration: Infinity, position: "bottom-left" }, + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [deleteActive, deleteCountdown]); + + function toggleDelete() { + setDeleteActive((prev) => { + if (!prev) { + pendingDeleteShape.current = shape; + setDeleteCountdown(DELETE_SECONDS); + } else { + pendingDeleteShape.current = null; + toast.dismiss(TOAST_ID); + } + return !prev; + }); + } + + function handleDelete() { + const target = pendingDeleteShape.current; + if (!target) return; + pendingDeleteShape.current = null; + onDeleted(target); + onOpenChange(false); + } + + function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + if (!shape) return; + + const updated: UserMarker = { + ...shape, + name: name.trim() || shape.name, + meta: { + ...shape.meta, + poi_type: status || undefined, + notes: notes.trim() || undefined, + }, + }; + + onUpdated(updated); + onOpenChange(false); + } + + return ( + { if (!v) onOpenChange(false); }}> + +
+ {/* Header */} + + {titleLabel} +

+ {shape ? `${shape.lat.toFixed(5)}, ${shape.lng.toFixed(5)}` : ""} +

+
+ + {/* Body */} +
+ {/* Name */} + + setName(e.target.value)} + required + className="bg-muted/50 border border-border focus-visible:border-ring" + /> + + + {/* Notes (optional, all types) */} + +