diff --git a/prisma/migrations/20260302000000_add_custom_search_terms/migration.sql b/prisma/migrations/20260302000000_add_custom_search_terms/migration.sql
new file mode 100644
index 0000000..3949713
--- /dev/null
+++ b/prisma/migrations/20260302000000_add_custom_search_terms/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "requests" ADD COLUMN "custom_search_terms" TEXT;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index a0d5a26..07d0d15 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -232,6 +232,7 @@ model Request {
importAttempts Int @default(0) @map("import_attempts")
maxImportRetries Int @default(5) @map("max_import_retries")
lastSearchAt DateTime? @map("last_search_at")
+ customSearchTerms String? @map("custom_search_terms") @db.Text
lastImportAt DateTime? @map("last_import_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
diff --git a/src/app/admin/components/AdjustSearchTermsModal.tsx b/src/app/admin/components/AdjustSearchTermsModal.tsx
new file mode 100644
index 0000000..cb8b6fa
--- /dev/null
+++ b/src/app/admin/components/AdjustSearchTermsModal.tsx
@@ -0,0 +1,154 @@
+/**
+ * Component: Adjust Search Terms Modal
+ * Documentation: documentation/admin-dashboard.md
+ */
+
+'use client';
+
+import { useState } from 'react';
+import { Modal } from '@/components/ui/Modal';
+import { fetchWithAuth } from '@/lib/utils/api';
+import { useToast } from '@/components/ui/Toast';
+
+interface AdjustSearchTermsModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ requestId: string;
+ title: string;
+ author: string;
+ currentSearchTerms?: string | null;
+ onSuccess?: () => void;
+}
+
+export function AdjustSearchTermsModal({
+ isOpen,
+ onClose,
+ requestId,
+ title,
+ author,
+ currentSearchTerms,
+ onSuccess,
+}: AdjustSearchTermsModalProps) {
+ const toast = useToast();
+ const [searchTerms, setSearchTerms] = useState(currentSearchTerms || title);
+ const [isSaving, setIsSaving] = useState(false);
+ const [isSavingAndSearching, setIsSavingAndSearching] = useState(false);
+
+ // Reset state when modal opens
+ const handleClose = () => {
+ setSearchTerms(currentSearchTerms || title);
+ onClose();
+ };
+
+ const save = async (triggerSearch: boolean) => {
+ const setter = triggerSearch ? setIsSavingAndSearching : setIsSaving;
+ setter(true);
+
+ try {
+ // If terms match the original title, clear the override
+ const termsToSave = searchTerms.trim() === title ? null : searchTerms.trim() || null;
+
+ const response = await fetchWithAuth(`/api/admin/requests/${requestId}/search-terms`, {
+ method: 'PATCH',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ searchTerms: termsToSave, triggerSearch }),
+ });
+
+ if (!response.ok) {
+ const errorData = await response.json();
+ throw new Error(errorData.message || 'Failed to update search terms');
+ }
+
+ const data = await response.json();
+
+ if (data.searchTriggered) {
+ toast.success('Search terms saved and search triggered');
+ } else {
+ toast.success('Search terms saved');
+ }
+
+ onSuccess?.();
+ onClose();
+ } catch (error) {
+ toast.error(`Failed to save: ${error instanceof Error ? error.message : 'Unknown error'}`);
+ } finally {
+ setter(false);
+ }
+ };
+
+ const handleReset = () => {
+ setSearchTerms(title);
+ };
+
+ const isLoading = isSaving || isSavingAndSearching;
+ const hasChanges = searchTerms.trim() !== (currentSearchTerms || title);
+ const isCustom = searchTerms.trim() !== title;
+
+ return (
+
+ Cleanup source files +
++ Delete original files after successful import +
+