Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ ALLOWED_SCRIPT_PATHS="scripts/"

# WebSocket Configuration
WEBSOCKET_PORT="3001"
GITHUB_TOKEN=your_github_token_here

# User settings
GITHUB_TOKEN=
SAVE_FILTER=false
FILTERS=
Binary file modified public/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions src/app/_components/DownloadedScriptsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export function DownloadedScriptsTab() {
sortBy: 'name',
sortOrder: 'asc',
});
const [saveFiltersEnabled, setSaveFiltersEnabled] = useState(false);
const [isLoadingFilters, setIsLoadingFilters] = useState(true);
const gridRef = useRef<HTMLDivElement>(null);

const { data: scriptCardsData, isLoading: githubLoading, error: githubError, refetch } = api.scripts.getScriptCardsWithCategories.useQuery();
Expand All @@ -29,6 +31,62 @@ export function DownloadedScriptsTab() {
{ enabled: !!selectedSlug }
);

// Load SAVE_FILTER setting and saved filters on component mount
useEffect(() => {
const loadSettings = async () => {
try {
// Load SAVE_FILTER setting
const saveFilterResponse = await fetch('/api/settings/save-filter');
let saveFilterEnabled = false;
if (saveFilterResponse.ok) {
const saveFilterData = await saveFilterResponse.json();
saveFilterEnabled = saveFilterData.enabled ?? false;
setSaveFiltersEnabled(saveFilterEnabled);
}

// Load saved filters if SAVE_FILTER is enabled
if (saveFilterEnabled) {
const filtersResponse = await fetch('/api/settings/filters');
if (filtersResponse.ok) {
const filtersData = await filtersResponse.json();
if (filtersData.filters) {
setFilters(filtersData.filters as FilterState);
}
}
}
} catch (error) {
console.error('Error loading settings:', error);
} finally {
setIsLoadingFilters(false);
}
};

void loadSettings();
}, []);

// Save filters when they change (if SAVE_FILTER is enabled)
useEffect(() => {
if (!saveFiltersEnabled || isLoadingFilters) return;

const saveFilters = async () => {
try {
await fetch('/api/settings/filters', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ filters }),
});
} catch (error) {
console.error('Error saving filters:', error);
}
};

// Debounce the save operation
const timeoutId = setTimeout(() => void saveFilters(), 500);
return () => clearTimeout(timeoutId);
}, [filters, saveFiltersEnabled, isLoadingFilters]);

// Extract categories from metadata
const categories = React.useMemo((): string[] => {
if (!scriptCardsData?.success || !scriptCardsData.metadata?.categories) return [];
Expand Down Expand Up @@ -341,6 +399,8 @@ export function DownloadedScriptsTab() {
totalScripts={downloadedScripts.length}
filteredCount={filteredScripts.length}
updatableCount={filterCounts.updatableCount}
saveFiltersEnabled={saveFiltersEnabled}
isLoadingFilters={isLoadingFilters}
/>

{/* Scripts Grid */}
Expand Down
26 changes: 26 additions & 0 deletions src/app/_components/FilterBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ interface FilterBarProps {
totalScripts: number;
filteredCount: number;
updatableCount?: number;
saveFiltersEnabled?: boolean;
isLoadingFilters?: boolean;
}

const SCRIPT_TYPES = [
Expand All @@ -33,6 +35,8 @@ export function FilterBar({
totalScripts,
filteredCount,
updatableCount = 0,
saveFiltersEnabled = false,
isLoadingFilters = false,
}: FilterBarProps) {
const [isTypeDropdownOpen, setIsTypeDropdownOpen] = useState(false);
const [isSortDropdownOpen, setIsSortDropdownOpen] = useState(false);
Expand Down Expand Up @@ -78,6 +82,28 @@ export function FilterBar({

return (
<div className="mb-6 rounded-lg border border-border bg-card p-4 sm:p-6 shadow-sm">
{/* Loading State */}
{isLoadingFilters && (
<div className="mb-4 flex items-center justify-center py-2">
<div className="flex items-center space-x-2 text-sm text-muted-foreground">
<div className="h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent"></div>
<span>Loading saved filters...</span>
</div>
</div>
)}

{/* Filter Persistence Status */}
{!isLoadingFilters && saveFiltersEnabled && (
<div className="mb-4 flex items-center justify-center py-1">
<div className="flex items-center space-x-2 text-xs text-green-600">
<svg className="h-3 w-3" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
<span>Filters are being saved automatically</span>
</div>
</div>
)}

{/* Search Bar */}
<div className="mb-4">
<div className="relative max-w-md w-full">
Expand Down
Loading