Просмотр исходного кода

Add printer status badges, remove SpoolBuddy inventory page

  - Add printer status badges to dashboard left column (green/gray
    online/offline dots, compact wrapping pills, overflow-hidden)
  - Remove SpoolBuddy inventory page — inventory management belongs
    in the main Bambuddy frontend, not the kiosk
  - Remove inventory nav item from bottom nav (now 3 tabs)
  - Replace "Add to Inventory" navigate with quick-add modal that
    creates a basic PLA spool entry via API, with a hint to use
    the main Bambuddy UI for full spool details instead
maziggy 6 месяцев назад
Родитель
Сommit
e1795ff3b7

+ 1 - 1
CHANGELOG.md

@@ -14,7 +14,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **SpoolBuddy On-Screen Keyboard** — Added a virtual QWERTY keyboard for the SpoolBuddy kiosk UI (and login page) since the Raspberry Pi has no physical keyboard and system-level virtual keyboards (squeekboard, wvkbd) don't auto-show/hide in the labwc/Chromium kiosk environment. Uses `react-simple-keyboard` with a dark theme matching the bambu-dark/bambu-green palette. Auto-shows when any text/password/email input is focused, supports shift, caps lock, backspace, and email-friendly keys (@, .). Inputs with `data-vkb="false"` are excluded (e.g. SpoolBuddySettingsPage's own numpad). A two-phase close prevents ghost-click passthrough to elements underneath the keyboard.
 - **SpoolBuddy Inline Spool Cards** — Placing an NFC-tagged spool on the SpoolBuddy reader now shows spool info directly in the dashboard's right panel instead of a separate modal overlay. Known spools display a SpoolIcon with color/brand/material, a large remaining-weight readout with fill bar, and a weight comparison grid, with action buttons for "Assign to AMS", "Sync Weight", and "Close". Unknown tags show the tag UID, scale weight, and offer "Add to Inventory" or "Link to Spool" actions. The card stays visible if the tag is removed (for continued interaction) and won't re-appear for the same tag after dismissal — but re-placing a tag after removal shows it again. The idle spool animation displays when no tag is detected.
 - **SpoolBuddy AMS Page: External Slots & Slot Configuration** — The SpoolBuddy AMS page (`/spoolbuddy/ams`) now displays external spool slots (single nozzle: "Ext", dual nozzle: "Ext-L"/"Ext-R") and AMS-HT units in a compact horizontal row below the regular AMS grid, fitting within the 1024×600 kiosk display without scrolling. Clicking any AMS, AMS-HT, or external slot opens the `ConfigureAmsSlotModal` to configure filament type and color — the same modal used on the main Printers page. Dual-nozzle printers show L/R nozzle badges on each AMS unit. Temperature and humidity are displayed with threshold-colored SVG icons (green/gold/red) matching the Bambu Lab style on the main printer cards, using the configured AMS humidity and temperature thresholds from settings.
-- **SpoolBuddy Dashboard Redesign** — Redesigned the SpoolBuddy dashboard with a two-column layout: left column shows device connection status (scale and NFC with state-colored icons — green when device is online, gray when offline) and a compact printers list with live status indicators; right column shows the current spool card. Cards use a dashed border style for a cleaner look. The large weight display card was removed in favor of the inline scale reading in the device card.
+- **SpoolBuddy Dashboard Redesign** — Redesigned the SpoolBuddy dashboard with a two-column layout: left column shows device connection status (scale and NFC with state-colored icons — green when device is online, gray when offline) and printer status badges below (compact pills with green/gray dots for online/offline, wrapping to fit without scrolling); right column shows the current spool card. Cards use a dashed border style for a cleaner look. The large weight display card was removed in favor of the inline scale reading in the device card. Unknown NFC tags now offer a quick-add modal that creates a basic PLA spool entry linked to the tag — with a hint recommending users add spools via the main Bambuddy UI first for full details. The separate SpoolBuddy inventory page was removed since inventory management belongs in the main Bambuddy frontend; the bottom nav now has three tabs (Dashboard, AMS, Settings).
 - **SpoolBuddy Kiosk Auth Bypass via API Key** — When Bambuddy auth is enabled, the SpoolBuddy kiosk (Chromium on RPi) was redirected to the login page because the `ProtectedRoute` requires a user object from `GET /auth/me`, which only accepted JWT tokens. The `/auth/me` endpoint now also accepts API keys (via `Authorization: Bearer bb_xxx` or `X-API-Key` header) and returns a synthetic admin user with all permissions. The frontend's `AuthContext` reads an optional `?token=` URL parameter on first load, stores it in localStorage, and strips it from the URL to prevent leakage via browser history or referrer. The install script now includes the API key in the kiosk URL (`/spoolbuddy?token=${API_KEY}`), so the device authenticates automatically on boot without manual login.
 - **Daily Beta Builds** — Added a release script (`docker-publish-daily-beta.sh`) that reads the current `APP_VERSION` from config, builds a multi-arch Docker image, pushes to both GHCR and Docker Hub, and creates/updates a GitHub prerelease with changelog notes. Daily builds overwrite the same beta version tag (e.g., `0.2.2b1`) — users pull the latest by re-pulling the tag or using Watchtower. Beta images are never tagged as `latest`.
 - **Inventory Scale Weight Check Column** — Added a "Weight Check" column (hidden by default) to the inventory table that compares each spool's last scale measurement against its calculated gross weight (net remaining + core weight). Spools within a ±50g tolerance show a green checkmark; mismatched spools show a yellow warning with the difference and a sync button that trusts the scale reading and resets weight tracking. The backend stores `last_scale_weight` and `last_weighed_at` on each spool whenever weight is synced via SpoolBuddy, and the column tooltip shows scale weight, calculated weight, and difference. Edge case: when scale weight is below core weight (empty spool or not on scale), the comparison treats it as a match since sync can't correct this.

+ 0 - 2
frontend/src/App.tsx

@@ -26,7 +26,6 @@ import { AuthProvider, useAuth } from './contexts/AuthContext';
 import { SpoolBuddyLayout } from './components/spoolbuddy/SpoolBuddyLayout';
 import { SpoolBuddyDashboard } from './pages/spoolbuddy/SpoolBuddyDashboard';
 import { SpoolBuddyAmsPage } from './pages/spoolbuddy/SpoolBuddyAmsPage';
-import { SpoolBuddyInventoryPage } from './pages/spoolbuddy/SpoolBuddyInventoryPage';
 import { SpoolBuddySettingsPage } from './pages/spoolbuddy/SpoolBuddySettingsPage';
 import { SpoolBuddyCalibrationPage } from './pages/spoolbuddy/SpoolBuddyCalibrationPage';
 const queryClient = new QueryClient({
@@ -123,7 +122,6 @@ function App() {
                 <Route element={<ProtectedRoute><WebSocketProvider><SpoolBuddyLayout /></WebSocketProvider></ProtectedRoute>}>
                   <Route path="spoolbuddy" element={<SpoolBuddyDashboard />} />
                   <Route path="spoolbuddy/ams" element={<SpoolBuddyAmsPage />} />
-                  <Route path="spoolbuddy/inventory" element={<SpoolBuddyInventoryPage />} />
                   <Route path="spoolbuddy/settings" element={<SpoolBuddySettingsPage />} />
                   <Route path="spoolbuddy/calibration" element={<SpoolBuddyCalibrationPage />} />
                 </Route>

+ 0 - 10
frontend/src/components/spoolbuddy/SpoolBuddyBottomNav.tsx

@@ -22,16 +22,6 @@ const navItems = [
       </svg>
     ),
   },
-  {
-    to: '/spoolbuddy/inventory',
-    labelKey: 'spoolbuddy.nav.inventory',
-    fallback: 'Inventory',
-    icon: (
-      <svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
-        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
-      </svg>
-    ),
-  },
   {
     to: '/spoolbuddy/settings',
     labelKey: 'spoolbuddy.nav.settings',

+ 126 - 5
frontend/src/pages/spoolbuddy/SpoolBuddyDashboard.tsx

@@ -1,9 +1,9 @@
 import { useState, useEffect, useMemo, useRef } from 'react';
-import { useOutletContext, useNavigate } from 'react-router-dom';
-import { useQuery } from '@tanstack/react-query';
+import { useOutletContext } from 'react-router-dom';
+import { useQuery, useQueries } from '@tanstack/react-query';
 import { useTranslation } from 'react-i18next';
 import type { SpoolBuddyOutletContext } from '../../components/spoolbuddy/SpoolBuddyLayout';
-import { api, type InventorySpool } from '../../api/client';
+import { api, type InventorySpool, type Printer } from '../../api/client';
 import { SpoolIcon } from '../../components/spoolbuddy/SpoolIcon';
 import { SpoolInfoCard, UnknownTagCard } from '../../components/spoolbuddy/SpoolInfoCard';
 import { AssignToAmsModal } from '../../components/spoolbuddy/AssignToAmsModal';
@@ -109,7 +109,6 @@ function DeviceOfflineState() {
 export function SpoolBuddyDashboard() {
   const { sbState, selectedPrinterId } = useOutletContext<SpoolBuddyOutletContext>();
   const { t } = useTranslation();
-  const navigate = useNavigate();
 
   // Fetch spools for stats, tag lookup, and untagged list
   const { data: spools = [], refetch: refetchSpools } = useQuery({
@@ -117,12 +116,28 @@ export function SpoolBuddyDashboard() {
     queryFn: () => api.getSpools(false),
   });
 
+  // Fetch printers and their statuses for the status badges
+  const { data: printers = [] } = useQuery({
+    queryKey: ['printers'],
+    queryFn: () => api.getPrinters(),
+  });
+
+  const statusQueries = useQueries({
+    queries: printers.map((printer: Printer) => ({
+      queryKey: ['printerStatus', printer.id],
+      queryFn: () => api.getPrinterStatus(printer.id),
+      refetchInterval: 10000,
+    })),
+  });
+
   // Current Spool card state - persists until user closes or new tag detected
   const [displayedTagId, setDisplayedTagId] = useState<string | null>(null);
   const [displayedWeight, setDisplayedWeight] = useState<number | null>(null);
   const [hiddenTagId, setHiddenTagId] = useState<string | null>(null);
   const [showLinkModal, setShowLinkModal] = useState(false);
   const [showAssignAmsModal, setShowAssignAmsModal] = useState(false);
+  const [showQuickAddModal, setShowQuickAddModal] = useState(false);
+  const [quickAddBusy, setQuickAddBusy] = useState(false);
 
   // Track current tag from state
   const currentTagId = sbState.matchedSpool?.tag_uid ?? sbState.unknownTagUid ?? null;
@@ -197,6 +212,46 @@ export function SpoolBuddyDashboard() {
     }
   };
 
+  const handleQuickAddToInventory = async () => {
+    if (!displayedTagId) return;
+    setQuickAddBusy(true);
+    try {
+      const weight = liveWeight ?? displayedWeight;
+      await api.createSpool({
+        material: 'PLA',
+        subtype: null,
+        color_name: null,
+        rgba: null,
+        brand: null,
+        label_weight: 1000,
+        core_weight: 250,
+        core_weight_catalog_id: null,
+        weight_used: 0,
+        slicer_filament: null,
+        slicer_filament_name: null,
+        nozzle_temp_min: null,
+        nozzle_temp_max: null,
+        note: null,
+        added_full: null,
+        last_used: null,
+        encode_time: null,
+        tag_uid: displayedTagId,
+        tray_uuid: null,
+        data_origin: 'spoolbuddy',
+        tag_type: 'generic',
+        cost_per_kg: null,
+        last_scale_weight: weight,
+        last_weighed_at: weight !== null ? new Date().toISOString() : null,
+      });
+      setShowQuickAddModal(false);
+      refetchSpools();
+    } catch (e) {
+      console.error('Failed to quick-add spool:', e);
+    } finally {
+      setQuickAddBusy(false);
+    }
+  };
+
   // For unknown tags, use live weight or stored displayed weight
   const useScaleWeight = currentWeight !== null &&
     (currentTagId === displayedTagId || (currentTagId === null && displayedTagId !== null));
@@ -273,6 +328,30 @@ export function SpoolBuddyDashboard() {
               </div>
             </div>
           </div>
+
+          {/* Printer status badges */}
+          {printers.length > 0 && (
+            <div className="mt-3 border border-dashed border-zinc-700/50 rounded-xl p-4">
+              <h2 className="text-sm font-semibold text-zinc-400 uppercase tracking-wide mb-2.5">
+                {t('spoolbuddy.dashboard.printers', 'Printers')}
+              </h2>
+              <div className="flex flex-wrap gap-2 overflow-hidden">
+                {printers.map((printer: Printer, i: number) => {
+                  const isOnline = statusQueries[i]?.data?.connected ?? false;
+                  return (
+                    <div
+                      key={printer.id}
+                      className="flex items-center gap-1.5 px-2.5 py-1 bg-zinc-800/50 rounded-lg"
+                      title={`${printer.name} — ${isOnline ? 'Online' : 'Offline'}`}
+                    >
+                      <div className={`w-2 h-2 rounded-full shrink-0 ${isOnline ? 'bg-green-500' : 'bg-zinc-600'}`} />
+                      <span className="text-xs text-zinc-400 truncate max-w-[100px]">{printer.name}</span>
+                    </div>
+                  );
+                })}
+              </div>
+            </div>
+          )}
         </div>
 
         {/* Right column: Current Spool */}
@@ -308,7 +387,7 @@ export function SpoolBuddyDashboard() {
                   tagUid={displayedTagId}
                   scaleWeight={liveWeight ?? displayedWeight}
                   onLinkSpool={untaggedSpools.length > 0 ? () => setShowLinkModal(true) : undefined}
-                  onAddToInventory={() => navigate(`/spoolbuddy/inventory?new=true&tag_uid=${displayedTagId}`)}
+                  onAddToInventory={() => setShowQuickAddModal(true)}
                   onClose={handleCloseSpoolCard}
                 />
               ) : (
@@ -339,6 +418,48 @@ export function SpoolBuddyDashboard() {
           onLink={handleLinkTagToSpool}
         />
       )}
+
+      {/* Quick-add to Inventory Modal */}
+      {showQuickAddModal && displayedTagId && (
+        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60">
+          <div className="bg-zinc-800 rounded-2xl p-6 mx-4 max-w-sm w-full border border-zinc-700">
+            <h3 className="text-lg font-semibold text-zinc-100 mb-3">
+              {t('spoolbuddy.modal.addToInventory', 'Add to Inventory')}
+            </h3>
+
+            {/* Hint */}
+            <div className="flex gap-2.5 p-3 bg-amber-500/10 border border-amber-500/20 rounded-lg mb-4">
+              <svg className="w-5 h-5 text-amber-500 shrink-0 mt-0.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
+              </svg>
+              <p className="text-sm text-amber-200/80">
+                {t('spoolbuddy.modal.quickAddHint', 'For best results, add the spool in the Bambuddy web interface first (with material, color, brand), then use "Link to Spool" here to assign the NFC tag.')}
+              </p>
+            </div>
+
+            <p className="text-sm text-zinc-400 mb-1">
+              {t('spoolbuddy.modal.quickAddDesc', 'This will create a basic PLA spool entry with this NFC tag. You can edit the details later in Bambuddy.')}
+            </p>
+            <p className="text-xs text-zinc-500 font-mono mb-5">{displayedTagId}</p>
+
+            <div className="flex gap-3">
+              <button
+                onClick={() => setShowQuickAddModal(false)}
+                className="flex-1 px-4 py-2.5 rounded-lg text-sm font-medium bg-zinc-700 text-zinc-300 hover:bg-zinc-600 transition-colors min-h-[44px]"
+              >
+                {t('common.cancel', 'Cancel')}
+              </button>
+              <button
+                onClick={handleQuickAddToInventory}
+                disabled={quickAddBusy}
+                className="flex-1 px-4 py-2.5 rounded-lg text-sm font-medium bg-green-600 text-white hover:bg-green-700 disabled:opacity-50 transition-colors min-h-[44px]"
+              >
+                {quickAddBusy ? t('common.saving', 'Saving...') : t('spoolbuddy.modal.addAnyway', 'Add Anyway')}
+              </button>
+            </div>
+          </div>
+        </div>
+      )}
     </div>
   );
 }

+ 0 - 160
frontend/src/pages/spoolbuddy/SpoolBuddyInventoryPage.tsx

@@ -1,160 +0,0 @@
-import { useState, useMemo } from 'react';
-import { useQuery } from '@tanstack/react-query';
-import { useTranslation } from 'react-i18next';
-import { api, type InventorySpool } from '../../api/client';
-import { SpoolIcon } from '../../components/spoolbuddy/SpoolIcon';
-
-function formatDate(dateStr: string | null): string {
-  if (!dateStr) return '-';
-  try {
-    const d = new Date(dateStr);
-    return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
-  } catch {
-    return '-';
-  }
-}
-
-function SpoolCard({ spool }: { spool: InventorySpool }) {
-  const { t } = useTranslation();
-  const [expanded, setExpanded] = useState(false);
-
-  const remaining = Math.max(0, spool.label_weight - spool.weight_used);
-  const remainingPct = spool.label_weight > 0 ? (remaining / spool.label_weight) * 100 : 0;
-  const colorHex = spool.rgba ? `#${spool.rgba.slice(0, 6)}` : '#808080';
-  const fillColor = remainingPct > 50 ? '#22c55e' : remainingPct > 15 ? '#f59e0b' : '#ef4444';
-
-  return (
-    <button
-      className="w-full bg-zinc-800 rounded-lg p-3.5 text-left transition-colors hover:bg-zinc-750 active:bg-zinc-700"
-      onClick={() => setExpanded(!expanded)}
-    >
-      <div className="flex items-center gap-3">
-        {/* Spool icon */}
-        <SpoolIcon color={colorHex} isEmpty={false} size={36} />
-
-        {/* Info */}
-        <div className="flex-1 min-w-0">
-          <div className="flex items-center gap-2">
-            <span className="text-base font-medium text-zinc-100 truncate">
-              {spool.color_name || spool.material}
-            </span>
-            {spool.tag_uid && (
-              <svg className="w-3 h-3 text-green-500 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
-                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A2 2 0 013 12V7a4 4 0 014-4z" />
-              </svg>
-            )}
-          </div>
-          <span className="text-sm text-zinc-500 truncate block">
-            {spool.brand ? `${spool.brand} \u2022 ` : ''}{spool.material}{spool.subtype ? ` ${spool.subtype}` : ''}
-          </span>
-        </div>
-
-        {/* Weight */}
-        <div className="text-right shrink-0">
-          <div className="text-base text-zinc-300">{Math.round(remaining)}g</div>
-          <div className="text-sm text-zinc-500">{t('spoolbuddy.spool.remaining', 'Remaining')}</div>
-        </div>
-      </div>
-
-      {/* Fill bar */}
-      <div className="w-full h-1.5 bg-zinc-700 rounded-full overflow-hidden mt-2">
-        <div
-          className="h-full rounded-full transition-all"
-          style={{ width: `${Math.min(100, remainingPct)}%`, backgroundColor: fillColor }}
-        />
-      </div>
-
-      {/* Expanded details */}
-      {expanded && (
-        <div className="mt-3 pt-3 border-t border-zinc-700 grid grid-cols-2 gap-x-4 gap-y-2 text-sm">
-          <div className="flex justify-between">
-            <span className="text-zinc-500">{t('spoolbuddy.spool.labelWeight', 'Label')}</span>
-            <span className="text-zinc-400">{spool.label_weight}g</span>
-          </div>
-          <div className="flex justify-between">
-            <span className="text-zinc-500">{t('spoolbuddy.spool.coreWeight', 'Core')}</span>
-            <span className="text-zinc-400">{spool.core_weight}g</span>
-          </div>
-          <div className="flex justify-between">
-            <span className="text-zinc-500">{t('spoolbuddy.spool.material', 'Material')}</span>
-            <span className="text-zinc-400">{spool.material}{spool.subtype ? ` ${spool.subtype}` : ''}</span>
-          </div>
-          <div className="flex justify-between">
-            <span className="text-zinc-500">{t('spoolbuddy.spool.lastUsed', 'Last used')}</span>
-            <span className="text-zinc-400">{formatDate(spool.last_used)}</span>
-          </div>
-          {spool.tag_uid && (
-            <div className="col-span-2 flex justify-between">
-              <span className="text-zinc-500">Tag</span>
-              <span className="text-zinc-400 font-mono text-xs">{spool.tag_uid}</span>
-            </div>
-          )}
-        </div>
-      )}
-    </button>
-  );
-}
-
-export function SpoolBuddyInventoryPage() {
-  const { t } = useTranslation();
-  const [search, setSearch] = useState('');
-
-  const { data: spools = [], isLoading } = useQuery({
-    queryKey: ['inventory-spools'],
-    queryFn: () => api.getSpools(false),
-  });
-
-  const filtered = useMemo(() => {
-    if (!search.trim()) return spools;
-    const q = search.toLowerCase();
-    return spools.filter((s) =>
-      s.material.toLowerCase().includes(q) ||
-      s.brand?.toLowerCase().includes(q) ||
-      s.color_name?.toLowerCase().includes(q)
-    );
-  }, [spools, search]);
-
-  return (
-    <div className="h-full flex flex-col p-4">
-      {/* Header */}
-      <div className="flex items-center justify-between mb-4">
-        <h1 className="text-xl font-semibold text-zinc-100">
-          {t('spoolbuddy.nav.inventory', 'Inventory')}
-        </h1>
-        <span className="text-sm text-zinc-500">{spools.length} {t('spoolbuddy.inventory.spools', 'spools')}</span>
-      </div>
-
-      {/* Search */}
-      <div className="relative mb-4">
-        <svg className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-zinc-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
-          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
-        </svg>
-        <input
-          type="text"
-          value={search}
-          onChange={(e) => setSearch(e.target.value)}
-          placeholder={t('spoolbuddy.inventory.search', 'Search spools...')}
-          className="w-full pl-10 pr-4 py-2.5 bg-zinc-800 border border-zinc-700 rounded-lg text-sm text-zinc-100 placeholder-zinc-500 focus:outline-none focus:border-green-500 min-h-[44px]"
-        />
-      </div>
-
-      {/* Spool list */}
-      <div className="flex-1 min-h-0 overflow-y-auto space-y-2">
-        {isLoading ? (
-          <div className="flex items-center justify-center h-32">
-            <span className="text-zinc-500">{t('common.loading', 'Loading...')}</span>
-          </div>
-        ) : filtered.length === 0 ? (
-          <div className="flex flex-col items-center justify-center h-32 text-zinc-500">
-            <svg className="w-10 h-10 mb-2 opacity-50" fill="none" viewBox="0 0 24 24" stroke="currentColor">
-              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
-            </svg>
-            <p className="text-sm">{search ? t('spoolbuddy.inventory.noResults', 'No matching spools') : t('spoolbuddy.inventory.empty', 'No spools in inventory')}</p>
-          </div>
-        ) : (
-          filtered.map((spool) => <SpoolCard key={spool.id} spool={spool} />)
-        )}
-      </div>
-    </div>
-  );
-}

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
static/assets/index-B2SGH3be.js


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
static/assets/index-Dtmjz8ii.css


Разница между файлами не показана из-за своего большого размера
+ 0 - 0
static/assets/index-DxG5AoTP.css


+ 2 - 2
static/index.html

@@ -23,8 +23,8 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-B7PRBmZ8.js"></script>
-    <link rel="stylesheet" crossorigin href="/assets/index-DxG5AoTP.css">
+    <script type="module" crossorigin src="/assets/index-B2SGH3be.js"></script>
+    <link rel="stylesheet" crossorigin href="/assets/index-Dtmjz8ii.css">
   </head>
   <body>
     <div id="root"></div>

Некоторые файлы не были показаны из-за большого количества измененных файлов