frontend_nyla/src/components/Prompts/promptsLogic.tsx

315 lines
9.7 KiB
TypeScript

import { useState, useMemo } from 'react';
import { usePrompts, usePromptOperations, Prompt } from '../../hooks/usePrompts';
import { useLanguage } from '../../contexts/LanguageContext';
import type { EditFieldConfig } from '../ui/Popup/EditForm';
// Helper function to determine if a prompt can be deleted
const isPromptDeletable = (prompt: Prompt): boolean => {
// Primary check: If backend explicitly sets _hideDelete, respect that
if (prompt._hideDelete === true) {
return false;
}
// Hardcoded list of the six default system prompt IDs that cannot be deleted
const systemPromptIds = [
"097d34f0-9fcc-4233-bf1e-e64e13818464",
"17546dc6-b792-40e1-9aa1-0fcc4860d0c1",
"17c42519-2bf6-49b4-83f9-3cde7498310c",
"2bb85d1e-4e02-4de8-98ae-0e815267d864",
"93343a5b-49f0-4dbf-9513-2ab5f6938fd8",
"cfb51260-486f-4b42-96fe-ef03f406dcf1"
];
// Check if this prompt is one of the system default prompts
return !systemPromptIds.includes(prompt.id);
};
import type {
PromptsLogicReturn,
PromptActionConfig,
PromptColumnConfig
} from './promptsTypes';
export function usePromptsLogic(): PromptsLogicReturn {
const { prompts, loading, error, refetch } = usePrompts();
const { t } = useLanguage();
const {
handlePromptDelete,
handlePromptUpdate,
deletingPrompts
} = usePromptOperations();
// Edit modal state
const [editModalOpen, setEditModalOpen] = useState(false);
const [editingPrompt, setEditingPrompt] = useState<Prompt | null>(null);
// Configure edit fields for prompt editing
const editPromptFields: EditFieldConfig[] = useMemo(() => [
{
key: 'name',
label: t('prompts.field.name', 'Prompt Name'),
type: 'string',
editable: true,
required: true,
validator: (value: string) => {
if (!value || value.trim() === '') {
return t('prompts.validation.nameRequired', 'Prompt name cannot be empty');
}
if (value.length > 100) {
return t('prompts.validation.nameTooLong', 'Prompt name cannot exceed 100 characters');
}
return null;
}
},
{
key: 'content',
label: t('prompts.field.content', 'Prompt Content'),
type: 'textarea',
editable: true,
required: true,
minRows: 4,
maxRows: 8,
validator: (value: string) => {
if (!value || value.trim() === '') {
return t('prompts.validation.contentRequired', 'Prompt content cannot be empty');
}
if (value.length > 10000) {
return t('prompts.validation.contentTooLong', 'Prompt content cannot exceed 10,000 characters');
}
return null;
}
}
], [t]);
// Configure columns for the prompts table
const columns: PromptColumnConfig[] = useMemo(() => [
{
key: 'name',
label: t('prompts.column.name', 'Name'),
type: 'string',
width: 200,
minWidth: 150,
maxWidth: 300,
sortable: true,
filterable: true,
searchable: true,
formatter: (value: string | undefined) => (
<span className="promptName">
{value || t('prompts.unnamed', 'Unnamed')}
</span>
)
},
{
key: 'content',
label: t('prompts.column.content', 'Content'),
type: 'string',
width: 400,
minWidth: 200,
maxWidth: 600,
sortable: true,
filterable: true,
searchable: true,
formatter: (value: string | undefined) => (
<span className="promptContent" title={value}>
{value && value.length > 100 ? `${value.substring(0, 100)}...` : value || '-'}
</span>
)
}
], [t]);
// Handle prompt actions
const handleDeletePrompt = async (prompt: Prompt) => {
const promptName = prompt.name || prompt.id;
if (window.confirm(t('prompts.delete.confirm', 'Are you sure you want to delete "{name}"?').replace('{name}', promptName))) {
const success = await handlePromptDelete(prompt.id);
if (success) {
refetch(); // Refresh the prompts list
}
}
};
// Handle single prompt deletion for bulk delete
const handleDeleteSingle = async (prompt: Prompt) => {
const promptName = prompt.name || prompt.id;
if (window.confirm(t('prompts.delete.confirm', 'Are you sure you want to delete "{name}"?').replace('{name}', promptName))) {
const success = await handlePromptDelete(prompt.id);
if (success) {
refetch(); // Refresh the prompts list
} else {
console.error('Delete failed for prompt:', prompt.id);
}
}
};
// Handle multiple prompt deletion
const handleDeleteMultiple = async (promptsToDelete: Prompt[]) => {
const promptCount = promptsToDelete.length;
if (window.confirm(t('prompts.delete.confirmMultiple', 'Are you sure you want to delete {count} prompts?').replace('{count}', promptCount.toString()))) {
// Start all delete operations simultaneously
const deletePromises = promptsToDelete.map(async (prompt) => {
try {
const success = await handlePromptDelete(prompt.id);
return { promptId: prompt.id, success };
} catch (error) {
console.error('Failed to delete prompt:', prompt.id, error);
return { promptId: prompt.id, success: false };
}
});
// Wait for all deletions to complete
const results = await Promise.all(deletePromises);
// Check if any deletions failed
const failedDeletions = results.filter(result => !result.success);
if (failedDeletions.length > 0) {
console.error('Some prompt deletions failed:', failedDeletions);
}
// Refresh the prompt list regardless of individual failures
refetch();
}
};
// Handle edit prompt
const handleEditPrompt = (prompt: Prompt) => {
setEditingPrompt(prompt);
setEditModalOpen(true);
};
// Handle save prompt
const handleSavePrompt = async (updatedPrompt: Prompt) => {
if (!editingPrompt) return;
try {
// Call API to update prompt - backend requires full Prompt object
const updateData = {
id: editingPrompt.id,
name: updatedPrompt.name,
content: updatedPrompt.content,
mandateId: editingPrompt.mandateId
};
const result = await handlePromptUpdate(editingPrompt.id, updateData);
if (result.success) {
// Close modal
setEditModalOpen(false);
setEditingPrompt(null);
// Refresh prompt list
await refetch();
// Notify other components that prompts have been updated
window.dispatchEvent(new CustomEvent('promptUpdated', {
detail: { promptId: editingPrompt.id, newName: updatedPrompt.name }
}));
} else {
console.error('Failed to update prompt:', result.error);
// TODO: Show error message to user
}
} catch (error) {
console.error('Failed to update prompt:', error);
// TODO: Show error message to user
}
};
// Handle cancel edit
const handleCancelEdit = () => {
setEditModalOpen(false);
setEditingPrompt(null);
};
// Handle copy prompt
const handleCopyPrompt = async (prompt: Prompt) => {
try {
// Use the modern Clipboard API if available
if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(prompt.content);
} else {
// Fallback for older browsers or non-secure contexts
const textArea = document.createElement('textarea');
textArea.value = prompt.content;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
textArea.style.top = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
}
// Show success feedback
const promptName = prompt.name || t('prompts.unnamed', 'Unnamed');
console.log(`Copied content of "${promptName}" to clipboard`);
// Optional: You could add a toast notification here in the future
// showToast(t('prompts.copy.success', 'Prompt content copied to clipboard'), 'success');
} catch (error) {
console.error('Failed to copy prompt content:', error);
// Optional: You could add a toast notification here in the future
// showToast(t('prompts.copy.error', 'Failed to copy prompt content'), 'error');
}
};
// Configure action buttons
const actions: PromptActionConfig[] = useMemo(() => [
{
type: 'edit',
title: t('prompts.action.edit', 'Edit'),
onAction: (row: Prompt) => {
handleEditPrompt(row);
}
},
{
type: 'copy',
title: t('prompts.action.copy', 'Copy'),
onAction: (row: Prompt) => {
handleCopyPrompt(row);
}
},
{
type: 'delete',
title: (row: Prompt) => {
const isDeletable = isPromptDeletable(row);
return isDeletable
? t('prompts.action.delete', 'Delete')
: t('prompts.action.delete.disabled', 'No permission to delete prompt');
},
disabled: (row: Prompt) => !isPromptDeletable(row)
// onAction is handled by FormGenerator for delete confirmation
},
] as any, [t, deletingPrompts, handleDeletePrompt, handleEditPrompt, handleCopyPrompt]);
return {
// Data
prompts,
loading,
error,
// Actions
handleDeleteSingle,
handleDeleteMultiple,
handleEditPrompt,
handleCopyPrompt,
// Refetch function
refetch,
// Additional data for rendering
columns,
actions,
// Edit modal state
editModalOpen,
editingPrompt,
editPromptFields,
// Edit modal actions
handleSavePrompt,
handleCancelEdit
};
}