Optimized frontend
This commit is contained in:
2
app.py
2
app.py
@@ -167,7 +167,7 @@ def create_app():
|
||||
app = FastAPI(
|
||||
title="Spotizerr API",
|
||||
description="Music download service API",
|
||||
version="1.0.0",
|
||||
version="3.0.0",
|
||||
lifespan=lifespan,
|
||||
redirect_slashes=True # Enable automatic trailing slash redirects
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "spotizerr-ui",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"version": "3.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import { createRouter, createRootRoute, createRoute } from "@tanstack/react-router";
|
||||
import { createRouter, createRootRoute, createRoute, lazyRouteComponent } from "@tanstack/react-router";
|
||||
import Root from "./routes/root";
|
||||
import { Album } from "./routes/album";
|
||||
import { Artist } from "./routes/artist";
|
||||
import { Track } from "./routes/track";
|
||||
import { Home } from "./routes/home";
|
||||
import { Config } from "./routes/config";
|
||||
import { Playlist } from "./routes/playlist";
|
||||
import { History } from "./routes/history";
|
||||
import { Watchlist } from "./routes/watchlist";
|
||||
import apiClient from "./lib/api-client";
|
||||
import type { SearchResult } from "./types/spotify";
|
||||
|
||||
// Lazy load route components for code splitting
|
||||
const Album = lazyRouteComponent(() => import("./routes/album").then(m => ({ default: m.Album })));
|
||||
const Artist = lazyRouteComponent(() => import("./routes/artist").then(m => ({ default: m.Artist })));
|
||||
const Track = lazyRouteComponent(() => import("./routes/track").then(m => ({ default: m.Track })));
|
||||
const Home = lazyRouteComponent(() => import("./routes/home").then(m => ({ default: m.Home })));
|
||||
const Config = lazyRouteComponent(() => import("./routes/config").then(m => ({ default: m.Config })));
|
||||
const Playlist = lazyRouteComponent(() => import("./routes/playlist").then(m => ({ default: m.Playlist })));
|
||||
const History = lazyRouteComponent(() => import("./routes/history").then(m => ({ default: m.History })));
|
||||
const Watchlist = lazyRouteComponent(() => import("./routes/watchlist").then(m => ({ default: m.Watchlist })));
|
||||
|
||||
const rootRoute = createRootRoute({
|
||||
component: Root,
|
||||
});
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useState, useEffect, useRef, Suspense, lazy } from "react";
|
||||
import { useSearch } from "@tanstack/react-router";
|
||||
import { GeneralTab } from "../components/config/GeneralTab";
|
||||
import { DownloadsTab } from "../components/config/DownloadsTab";
|
||||
import { FormattingTab } from "../components/config/FormattingTab";
|
||||
import { AccountsTab } from "../components/config/AccountsTab";
|
||||
import { WatchTab } from "../components/config/WatchTab";
|
||||
import { ServerTab } from "../components/config/ServerTab";
|
||||
import { UserManagementTab } from "../components/config/UserManagementTab";
|
||||
import { ProfileTab } from "../components/config/ProfileTab";
|
||||
import { useSettings } from "../contexts/settings-context";
|
||||
import { useAuth } from "../contexts/auth-context";
|
||||
import { LoginScreen } from "../components/auth/LoginScreen";
|
||||
|
||||
// Lazy load config tab components for better code splitting
|
||||
const GeneralTab = lazy(() => import("../components/config/GeneralTab").then(m => ({ default: m.GeneralTab })));
|
||||
const DownloadsTab = lazy(() => import("../components/config/DownloadsTab").then(m => ({ default: m.DownloadsTab })));
|
||||
const FormattingTab = lazy(() => import("../components/config/FormattingTab").then(m => ({ default: m.FormattingTab })));
|
||||
const AccountsTab = lazy(() => import("../components/config/AccountsTab").then(m => ({ default: m.AccountsTab })));
|
||||
const WatchTab = lazy(() => import("../components/config/WatchTab").then(m => ({ default: m.WatchTab })));
|
||||
const ServerTab = lazy(() => import("../components/config/ServerTab").then(m => ({ default: m.ServerTab })));
|
||||
const UserManagementTab = lazy(() => import("../components/config/UserManagementTab").then(m => ({ default: m.UserManagementTab })));
|
||||
const ProfileTab = lazy(() => import("../components/config/ProfileTab").then(m => ({ default: m.ProfileTab })));
|
||||
|
||||
// Loading component for tab transitions
|
||||
const TabLoading = () => (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const ConfigComponent = () => {
|
||||
const { tab } = useSearch({ from: "/config" });
|
||||
const { user, isAuthenticated, authEnabled, isLoading: authLoading } = useAuth();
|
||||
@@ -102,11 +111,19 @@ const ConfigComponent = () => {
|
||||
const renderTabContent = () => {
|
||||
// User management and profile don't need config data
|
||||
if (activeTab === "user-management") {
|
||||
return <UserManagementTab />;
|
||||
return (
|
||||
<Suspense fallback={<TabLoading />}>
|
||||
<UserManagementTab />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
if (activeTab === "profile") {
|
||||
return <ProfileTab />;
|
||||
return (
|
||||
<Suspense fallback={<TabLoading />}>
|
||||
<ProfileTab />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading) return <div className="text-center py-12"><p className="text-content-muted dark:text-content-muted-dark">Loading configuration...</p></div>;
|
||||
@@ -114,17 +131,41 @@ const ConfigComponent = () => {
|
||||
|
||||
switch (activeTab) {
|
||||
case "general":
|
||||
return <GeneralTab config={config} isLoading={isLoading} />;
|
||||
return (
|
||||
<Suspense fallback={<TabLoading />}>
|
||||
<GeneralTab config={config} isLoading={isLoading} />
|
||||
</Suspense>
|
||||
);
|
||||
case "downloads":
|
||||
return <DownloadsTab config={config} isLoading={isLoading} />;
|
||||
return (
|
||||
<Suspense fallback={<TabLoading />}>
|
||||
<DownloadsTab config={config} isLoading={isLoading} />
|
||||
</Suspense>
|
||||
);
|
||||
case "formatting":
|
||||
return <FormattingTab config={config} isLoading={isLoading} />;
|
||||
return (
|
||||
<Suspense fallback={<TabLoading />}>
|
||||
<FormattingTab config={config} isLoading={isLoading} />
|
||||
</Suspense>
|
||||
);
|
||||
case "accounts":
|
||||
return <AccountsTab />;
|
||||
return (
|
||||
<Suspense fallback={<TabLoading />}>
|
||||
<AccountsTab />
|
||||
</Suspense>
|
||||
);
|
||||
case "watch":
|
||||
return <WatchTab />;
|
||||
return (
|
||||
<Suspense fallback={<TabLoading />}>
|
||||
<WatchTab />
|
||||
</Suspense>
|
||||
);
|
||||
case "server":
|
||||
return <ServerTab />;
|
||||
return (
|
||||
<Suspense fallback={<TabLoading />}>
|
||||
<ServerTab />
|
||||
</Suspense>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -99,6 +99,52 @@ export default defineConfig({
|
||||
"@": resolve(__dirname, "./src"),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
chunkSizeWarningLimit: 1000, // Increase warning limit to 1MB
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
// Core React and routing
|
||||
'react-vendor': ['react', 'react-dom'],
|
||||
'router-vendor': ['@tanstack/react-router'],
|
||||
|
||||
// Query and state management
|
||||
'query-vendor': ['@tanstack/react-query'],
|
||||
|
||||
// UI and icon libraries
|
||||
'ui-vendor': ['lucide-react', 'react-icons', 'sonner'],
|
||||
|
||||
// Table components (only used in specific routes)
|
||||
'table-vendor': ['@tanstack/react-table'],
|
||||
|
||||
// Form handling
|
||||
'form-vendor': ['react-hook-form', 'use-debounce'],
|
||||
|
||||
// HTTP client
|
||||
'http-vendor': ['axios'],
|
||||
|
||||
// Config components (heavy route with many tabs)
|
||||
'config-components': [
|
||||
'./src/components/config/GeneralTab',
|
||||
'./src/components/config/DownloadsTab',
|
||||
'./src/components/config/FormattingTab',
|
||||
'./src/components/config/AccountsTab',
|
||||
'./src/components/config/WatchTab',
|
||||
'./src/components/config/ServerTab',
|
||||
'./src/components/config/UserManagementTab',
|
||||
'./src/components/config/ProfileTab'
|
||||
],
|
||||
|
||||
// Utilities and helpers
|
||||
'utils-vendor': ['uuid'],
|
||||
},
|
||||
// Additional chunk optimization
|
||||
chunkFileNames: () => {
|
||||
return `assets/[name]-[hash].js`;
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
server: {
|
||||
host: true,
|
||||
port: 5173,
|
||||
|
||||
Reference in New Issue
Block a user