diff --git a/src/app/_components/CategorySidebar.tsx b/src/app/_components/CategorySidebar.tsx index 6b0307a..ed3883c 100644 --- a/src/app/_components/CategorySidebar.tsx +++ b/src/app/_components/CategorySidebar.tsx @@ -1,6 +1,7 @@ 'use client'; import { useState } from 'react'; +import { ContextualHelpIcon } from './ContextualHelpIcon'; interface CategorySidebarProps { categories: string[]; @@ -201,9 +202,12 @@ export function CategorySidebar({ {/* Header */}
{!isCollapsed && ( -
-

Categories

-

{totalScripts} Total scripts

+
+
+

Categories

+

{totalScripts} Total scripts

+
+
)} + + setIsOpen(false)} + initialSection={section} + /> + + ); +} diff --git a/src/app/_components/FilterBar.tsx b/src/app/_components/FilterBar.tsx index 72b66f7..2a03642 100644 --- a/src/app/_components/FilterBar.tsx +++ b/src/app/_components/FilterBar.tsx @@ -2,6 +2,7 @@ import React, { useState } from "react"; import { Button } from "./ui/button"; +import { ContextualHelpIcon } from "./ContextualHelpIcon"; import { Package, Monitor, Wrench, Server, FileText, Calendar, RefreshCw, Filter } from "lucide-react"; export interface FilterState { @@ -104,6 +105,14 @@ export function FilterBar({
)} + {/* Filter Header */} + {!isLoadingFilters && ( +
+

Filter Scripts

+ +
+ )} + {/* Search Bar */}
diff --git a/src/app/_components/Footer.tsx b/src/app/_components/Footer.tsx new file mode 100644 index 0000000..90509d3 --- /dev/null +++ b/src/app/_components/Footer.tsx @@ -0,0 +1,64 @@ +'use client'; + +import { api } from '~/trpc/react'; +import { Button } from './ui/button'; +import { ExternalLink, FileText } from 'lucide-react'; + +interface FooterProps { + onOpenReleaseNotes: () => void; +} + +export function Footer({ onOpenReleaseNotes }: FooterProps) { + const { data: versionData } = api.version.getCurrentVersion.useQuery(); + + return ( +
+
+
+
+ © 2024 PVE Scripts Local + {versionData?.success && versionData.version && ( + + )} +
+ +
+ + + +
+
+
+
+ ); +} diff --git a/src/app/_components/GeneralSettingsModal.tsx b/src/app/_components/GeneralSettingsModal.tsx index bfe710f..f2d105b 100644 --- a/src/app/_components/GeneralSettingsModal.tsx +++ b/src/app/_components/GeneralSettingsModal.tsx @@ -4,6 +4,7 @@ import { useState, useEffect } from 'react'; import { Button } from './ui/button'; import { Input } from './ui/input'; import { Toggle } from './ui/toggle'; +import { ContextualHelpIcon } from './ContextualHelpIcon'; interface GeneralSettingsModalProps { isOpen: boolean; @@ -280,7 +281,10 @@ export function GeneralSettingsModal({ isOpen, onClose }: GeneralSettingsModalPr
{/* Header */}
-

Settings

+
+

Settings

+ +
+
+ + setIsOpen(false)} + initialSection={initialSection} + /> + + ); +} diff --git a/src/app/_components/HelpModal.tsx b/src/app/_components/HelpModal.tsx new file mode 100644 index 0000000..3c3fd9f --- /dev/null +++ b/src/app/_components/HelpModal.tsx @@ -0,0 +1,492 @@ +'use client'; + +import { useState } from 'react'; +import { Button } from './ui/button'; +import { HelpCircle, Server, Settings, RefreshCw, Package, HardDrive, FolderOpen, Search, Download } from 'lucide-react'; + +interface HelpModalProps { + isOpen: boolean; + onClose: () => void; + initialSection?: string; +} + +type HelpSection = 'server-settings' | 'general-settings' | 'sync-button' | 'available-scripts' | 'downloaded-scripts' | 'installed-scripts' | 'update-system'; + +export function HelpModal({ isOpen, onClose, initialSection = 'server-settings' }: HelpModalProps) { + const [activeSection, setActiveSection] = useState(initialSection as HelpSection); + + if (!isOpen) return null; + + const sections = [ + { id: 'server-settings' as HelpSection, label: 'Server Settings', icon: Server }, + { id: 'general-settings' as HelpSection, label: 'General Settings', icon: Settings }, + { id: 'sync-button' as HelpSection, label: 'Sync Button', icon: RefreshCw }, + { id: 'available-scripts' as HelpSection, label: 'Available Scripts', icon: Package }, + { id: 'downloaded-scripts' as HelpSection, label: 'Downloaded Scripts', icon: HardDrive }, + { id: 'installed-scripts' as HelpSection, label: 'Installed Scripts', icon: FolderOpen }, + { id: 'update-system' as HelpSection, label: 'Update System', icon: Download }, + ]; + + const renderContent = () => { + switch (activeSection) { + case 'server-settings': + return ( +
+
+

Server Settings

+

+ Manage your Proxmox VE servers and configure connection settings. +

+
+ +
+
+

Adding PVE Servers

+
    +
  • Server Name: A friendly name to identify your server
  • +
  • IP Address: The IP address or hostname of your PVE server
  • +
  • Username: PVE user account (usually root or a dedicated user)
  • +
  • SSH Port: Default is 22, change if your server uses a different port
  • +
+
+ +
+

Authentication Types

+
    +
  • Password: Use username and password authentication
  • +
  • SSH Key: Use SSH key pair for secure authentication
  • +
  • Both: Try SSH key first, fallback to password if needed
  • +
+
+ +
+

Server Color Coding

+

+ Assign colors to servers for visual distinction throughout the application. + This helps identify which server you're working with when managing scripts. + This needs to be enabled in the General Settings. +

+
+
+
+ ); + + case 'general-settings': + return ( +
+
+

General Settings

+

+ Configure application preferences and behavior. +

+
+ +
+
+

Save Filters

+

+ When enabled, your script filter preferences (search terms, categories, sorting) + will be automatically saved and restored when you return to the application. +

+
    +
  • • Search queries are preserved
  • +
  • • Selected script types are remembered
  • +
  • • Sort preferences are maintained
  • +
  • • Category selections are saved
  • +
+
+ +
+

Server Color Coding

+

+ Enable visual color coding for servers throughout the application. + This makes it easier to identify which server you're working with. +

+
+ +
+

GitHub Integration

+

+ Add a GitHub Personal Access Token to increase API rate limits and improve performance. +

+
    +
  • • Bypasses GitHub's rate limiting for unauthenticated requests
  • +
  • • Improves script loading and syncing performance
  • +
  • • Token is stored securely and only used for API calls
  • +
+
+ +
+

Authentication

+

+ Secure your application with username and password authentication. +

+
    +
  • • Set up username and password for app access
  • +
  • • Enable/disable authentication as needed
  • +
  • • Credentials are stored securely
  • +
+
+
+
+ ); + + case 'sync-button': + return ( +
+
+

Sync Button

+

+ Synchronize script metadata from the ProxmoxVE GitHub repository. +

+
+ +
+
+

What Does Syncing Do?

+
    +
  • Updates Script Metadata: Downloads the latest script information (JSON files)
  • +
  • Refreshes Available Scripts: Updates the list of scripts you can download
  • +
  • Updates Categories: Refreshes script categories and organization
  • +
  • Checks for Updates: Identifies which downloaded scripts have newer versions
  • +
+
+ +
+

Important Notes

+
    +
  • Metadata Only: Syncing only updates script information, not the actual script files
  • +
  • No Downloads: Script files are downloaded separately when you choose to install them
  • +
  • Last Sync Time: Shows when the last successful sync occurred
  • +
  • Rate Limits: GitHub API limits may apply without a personal access token
  • +
+
+ +
+

When to Sync

+
    +
  • • When you want to see the latest available scripts
  • +
  • • To check for updates to your downloaded scripts
  • +
  • • If you notice scripts are missing or outdated
  • +
  • • After the ProxmoxVE repository has been updated
  • +
+
+
+
+ ); + + case 'available-scripts': + return ( +
+
+

Available Scripts

+

+ Browse and discover scripts from the ProxmoxVE repository. +

+
+ +
+
+

Browsing Scripts

+
    +
  • Category Sidebar: Filter scripts by category (Storage, Network, Security, etc.)
  • +
  • Search: Find scripts by name or description
  • +
  • View Modes: Switch between card and list view
  • +
  • Sorting: Sort by name or creation date
  • +
+
+ +
+

Filtering Options

+
    +
  • Script Types: Filter by CT (Container) or other script types
  • +
  • Update Status: Show only scripts with available updates
  • +
  • Search Query: Search within script names and descriptions
  • +
  • Categories: Filter by specific script categories
  • +
+
+ +
+

Script Actions

+
    +
  • View Details: Click on a script to see full information and documentation
  • +
  • Download: Download script files to your local system
  • +
  • Install: Run scripts directly on your PVE servers
  • +
  • Preview: View script content before downloading
  • +
+
+
+
+ ); + + case 'downloaded-scripts': + return ( +
+
+

Downloaded Scripts

+

+ Manage scripts that have been downloaded to your local system. +

+
+ +
+
+

What Are Downloaded Scripts?

+

+ These are scripts that you've downloaded from the repository and are stored locally on your system. +

+
    +
  • • Script files are stored in your local scripts directory
  • +
  • • You can run these scripts on your PVE servers
  • +
  • • Scripts can be updated when newer versions are available
  • +
+
+ +
+

Update Detection

+

+ The system automatically checks if newer versions of your downloaded scripts are available. +

+
    +
  • • Scripts with updates available are marked with an update indicator
  • +
  • • You can filter to show only scripts with available updates
  • +
  • • Update detection happens when you sync with the repository
  • +
+
+ +
+

Managing Downloaded Scripts

+
    +
  • Update Scripts: Download the latest version of a script
  • +
  • View Details: See script information and documentation
  • +
  • Install/Run: Execute scripts on your PVE servers
  • +
  • Filter & Search: Use the same filtering options as Available Scripts
  • +
+
+
+
+ ); + + case 'installed-scripts': + return ( +
+
+

Installed Scripts

+

+ Track and manage scripts that are installed on your PVE servers. +

+
+ +
+
+

+ + Auto-Detection (Primary Feature) +

+

+ The system can automatically detect LXC containers that have community-script tags on your PVE servers. +

+
    +
  • Automatic Discovery: Scans your PVE servers for containers with community-script tags
  • +
  • Container Detection: Identifies LXC containers running Proxmox helper scripts
  • +
  • Server Association: Links detected scripts to the specific PVE server
  • +
  • Bulk Import: Automatically creates records for all detected scripts
  • +
+
+

How Auto-Detection Works:

+
    +
  1. 1. Connects to your configured PVE servers
  2. +
  3. 2. Scans LXC container configurations
  4. +
  5. 3. Looks for containers with community-script tags
  6. +
  7. 4. Creates installed script records automatically
  8. +
+
+
+ +
+

Manual Script Management

+
    +
  • Add Scripts Manually: Create records for scripts not auto-detected
  • +
  • Edit Script Details: Update script names and container IDs
  • +
  • Delete Scripts: Remove scripts from tracking
  • +
  • Bulk Operations: Clean up old or invalid script records
  • +
+
+ +
+

Script Tracking Features

+
    +
  • Installation Status: Track success, failure, or in-progress installations
  • +
  • Server Association: Know which server each script is installed on
  • +
  • Container ID: Link scripts to specific LXC containers
  • +
  • Execution Logs: View output and logs from script installations
  • +
  • Filtering: Filter by server, status, or search terms
  • +
+
+ +
+

Managing Installed Scripts

+
    +
  • View All Scripts: See all tracked scripts across all servers
  • +
  • Filter by Server: Show scripts for a specific PVE server
  • +
  • Filter by Status: Show successful, failed, or in-progress installations
  • +
  • Sort Options: Sort by name, container ID, server, status, or date
  • +
  • Update Scripts: Re-run or update existing script installations
  • +
+
+
+
+ ); + + case 'update-system': + return ( +
+
+

Update System

+

+ Keep your PVE Scripts Management application up to date with the latest features and improvements. +

+
+ +
+
+

What Does Updating Do?

+
    +
  • Downloads Latest Version: Fetches the newest release from the GitHub repository
  • +
  • Updates Application Files: Replaces current files with the latest version
  • +
  • Installs Dependencies: Updates Node.js packages and dependencies
  • +
  • Rebuilds Application: Compiles the application with latest changes
  • +
  • Restarts Server: Automatically restarts the application server
  • +
+
+ +
+

How to Update

+
+
+
Automatic Update (Recommended)
+
    +
  • • Click the "Update Now" button when an update is available
  • +
  • • The system will handle everything automatically
  • +
  • • You'll see a progress overlay with update logs
  • +
  • • The page will reload automatically when complete
  • +
+
+ +
+
Manual Update (Advanced)
+

If automatic update fails, you can update manually:

+
+
# Navigate to the application directory
+
cd $PVESCRIPTLOCAL_DIR
+
# Pull latest changes
+
git pull
+
# Install dependencies
+
npm install
+
# Build the application
+
npm run build
+
# Start the application
+
npm start
+
+
+
+
+ +
+

Update Process

+
    +
  1. 1. Check for Updates: System automatically checks GitHub for new releases
  2. +
  3. 2. Download Update: Downloads the latest release files
  4. +
  5. 3. Backup Current Version: Creates backup of current installation
  6. +
  7. 4. Install New Version: Replaces files and updates dependencies
  8. +
  9. 5. Build Application: Compiles the updated code
  10. +
  11. 6. Restart Server: Stops old server and starts new version
  12. +
  13. 7. Reload Page: Automatically refreshes the browser
  14. +
+
+ +
+

Release Notes

+

+ Click the external link icon next to the update button to view detailed release notes on GitHub. +

+
    +
  • • See what's new in each version
  • +
  • • Read about bug fixes and improvements
  • +
  • • Check for any breaking changes
  • +
  • • View installation requirements
  • +
+
+ +
+

Important Notes

+
    +
  • Backup: Your data and settings are preserved during updates
  • +
  • Downtime: Brief downtime occurs during the update process
  • +
  • Compatibility: Updates maintain backward compatibility with your data
  • +
  • Rollback: If issues occur, you can manually revert to previous version
  • +
+
+
+
+ ); + + default: + return null; + } + }; + + return ( +
+
+ {/* Header */} +
+

+ + Help & Documentation +

+ +
+ +
+ {/* Sidebar Navigation */} +
+ +
+ + {/* Content Area */} +
+
+ {renderContent()} +
+
+
+
+
+ ); +} diff --git a/src/app/_components/ReleaseNotesModal.tsx b/src/app/_components/ReleaseNotesModal.tsx new file mode 100644 index 0000000..d46ce3b --- /dev/null +++ b/src/app/_components/ReleaseNotesModal.tsx @@ -0,0 +1,202 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { api } from '~/trpc/react'; +import { Button } from './ui/button'; +import { Badge } from './ui/badge'; +import { X, ExternalLink, Calendar, Tag, Loader2 } from 'lucide-react'; + +interface ReleaseNotesModalProps { + isOpen: boolean; + onClose: () => void; + highlightVersion?: string; +} + +interface Release { + tagName: string; + name: string; + publishedAt: string; + htmlUrl: string; + body: string; +} + +// Helper functions for localStorage +const getLastSeenVersion = (): string | null => { + if (typeof window === 'undefined') return null; + return localStorage.getItem('LAST_SEEN_RELEASE_VERSION'); +}; + +const markVersionAsSeen = (version: string): void => { + if (typeof window === 'undefined') return; + localStorage.setItem('LAST_SEEN_RELEASE_VERSION', version); +}; + +export function ReleaseNotesModal({ isOpen, onClose, highlightVersion }: ReleaseNotesModalProps) { + const [currentVersion, setCurrentVersion] = useState(null); + const { data: releasesData, isLoading, error } = api.version.getAllReleases.useQuery(undefined, { + enabled: isOpen + }); + const { data: versionData } = api.version.getCurrentVersion.useQuery(undefined, { + enabled: isOpen + }); + + // Get current version when modal opens + useEffect(() => { + if (isOpen && versionData?.success && versionData.version) { + setCurrentVersion(versionData.version); + } + }, [isOpen, versionData]); + + // Mark version as seen when modal closes + const handleClose = () => { + if (currentVersion) { + markVersionAsSeen(currentVersion); + } + onClose(); + }; + + if (!isOpen) return null; + + const releases: Release[] = releasesData?.success ? releasesData.releases ?? [] : []; + + return ( +
+
+ {/* Header */} +
+
+ +

Release Notes

+
+ +
+ + {/* Content */} +
+ {isLoading ? ( +
+
+ + Loading release notes... +
+
+ ) : error || !releasesData?.success ? ( +
+
+

Failed to load release notes

+

+ {releasesData?.error ?? 'Please try again later'} +

+
+
+ ) : releases.length === 0 ? ( +
+

No releases found

+
+ ) : ( +
+ {releases.map((release, index) => { + const isHighlighted = highlightVersion && release.tagName.replace('v', '') === highlightVersion; + const isLatest = index === 0; + + return ( +
+ {/* Release Header */} +
+
+
+

+ {release.name || release.tagName} +

+ {isLatest && ( + + Latest + + )} + {isHighlighted && ( + + New + + )} +
+
+
+ + {release.tagName} +
+
+ + + {new Date(release.publishedAt).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric' + })} + +
+
+
+ +
+ + {/* Release Body */} + {release.body && ( +
+
+ {release.body} +
+
+ )} +
+ ); + })} +
+ )} +
+ + {/* Footer */} +
+
+ {currentVersion && ( + Current version: v{currentVersion} + )} +
+ +
+
+
+ ); +} + +// Export helper functions for use in other components +export { getLastSeenVersion, markVersionAsSeen }; diff --git a/src/app/_components/ResyncButton.tsx b/src/app/_components/ResyncButton.tsx index 88c0fbb..8129be1 100644 --- a/src/app/_components/ResyncButton.tsx +++ b/src/app/_components/ResyncButton.tsx @@ -3,6 +3,7 @@ import { useState } from 'react'; import { api } from '~/trpc/react'; import { Button } from './ui/button'; +import { ContextualHelpIcon } from './ContextualHelpIcon'; export function ResyncButton() { const [isResyncing, setIsResyncing] = useState(false); @@ -44,27 +45,30 @@ export function ResyncButton() { Sync scripts with ProxmoxVE repo
- +
+ + +
{lastSync && (
diff --git a/src/app/_components/SettingsModal.tsx b/src/app/_components/SettingsModal.tsx index 4afd746..5457e9f 100644 --- a/src/app/_components/SettingsModal.tsx +++ b/src/app/_components/SettingsModal.tsx @@ -5,6 +5,7 @@ import type { Server, CreateServerData } from '../../types/server'; import { ServerForm } from './ServerForm'; import { ServerList } from './ServerList'; import { Button } from './ui/button'; +import { ContextualHelpIcon } from './ContextualHelpIcon'; interface SettingsModalProps { isOpen: boolean; @@ -106,7 +107,10 @@ export function SettingsModal({ isOpen, onClose }: SettingsModalProps) {
{/* Header */}
-

Settings

+
+

Settings

+ +
+ +
+ Release Notes: (null); const [activeTab, setActiveTab] = useState<'scripts' | 'downloaded' | 'installed'>('scripts'); + const [releaseNotesOpen, setReleaseNotesOpen] = useState(false); + const [highlightVersion, setHighlightVersion] = useState(undefined); const terminalRef = useRef(null); // Fetch data for script counts const { data: scriptCardsData } = api.scripts.getScriptCardsWithCategories.useQuery(); const { data: localScriptsData } = api.scripts.getAllDownloadedScripts.useQuery(); const { data: installedScriptsData } = api.installedScripts.getAllInstalledScripts.useQuery(); + const { data: versionData } = api.version.getCurrentVersion.useQuery(); + + // Auto-show release notes modal after update + useEffect(() => { + if (versionData?.success && versionData.version) { + const currentVersion = versionData.version; + const lastSeenVersion = getLastSeenVersion(); + + // If we have a current version and either no last seen version or versions don't match + if (currentVersion && (!lastSeenVersion || currentVersion !== lastSeenVersion)) { + setHighlightVersion(currentVersion); + setReleaseNotesOpen(true); + } + } + }, [versionData]); + + const handleOpenReleaseNotes = () => { + setHighlightVersion(undefined); + setReleaseNotesOpen(true); + }; + + const handleCloseReleaseNotes = () => { + setReleaseNotesOpen(false); + setHighlightVersion(undefined); + }; // Calculate script counts const scriptCounts = { @@ -83,7 +114,7 @@ export default function Home() { Manage and execute Proxmox helper scripts locally with live output streaming

- +
@@ -93,6 +124,7 @@ export default function Home() { +
@@ -100,54 +132,63 @@ export default function Home() {
@@ -179,6 +220,16 @@ export default function Home() { )}
+ + {/* Footer */} +