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
2,744 changes: 1,811 additions & 933 deletions BetterChatGPT/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion BetterChatGPT/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,4 @@ function App() {
);
}

export default App;
export default App;
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// src/components/SystemPromptLibraryMenu/ExportPrompt.tsx

import React, { useState } from 'react';
import useStore from '@store/store';
import { exportPrompts } from '@utils/prompt';

const ExportPrompt: React.FC = () => {
const prompts = useStore(state => state.prompts);
const [format, setFormat] = useState<'csv' | 'json'>('csv');
const [exportStatus, setExportStatus] = useState<string | null>(null);

const handleFormatChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
setFormat(e.target.value as 'csv' | 'json');
};

const handleExport = () => {
try {
if (prompts.length === 0) {
setExportStatus('No prompts to export');
return;
}

// Generate export content
const exportContent = exportPrompts(prompts, format);
if (!exportContent) {
setExportStatus('Failed to generate export content');
return;
}

// Create download link
const blob = new Blob(
[exportContent],
{ type: format === 'csv' ? 'text/csv' : 'application/json' }
);
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `prompts.${format}`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);

setExportStatus(`Successfully exported ${prompts.length} prompts as ${format.toUpperCase()}`);

// Clear status after 3 seconds
setTimeout(() => {
setExportStatus(null);
}, 3000);
} catch (error) {
setExportStatus(`Error exporting prompts: ${error instanceof Error ? error.message : String(error)}`);
}
};

return (
<div className="p-4 border rounded-lg bg-white dark:bg-gray-800">
<h2 className="text-xl font-bold mb-4">Export Prompts</h2>

<div className="mb-4">
<label className="block text-sm font-medium mb-1">Export Format</label>
<select
value={format}
onChange={handleFormatChange}
className="w-full px-3 py-2 border rounded-md"
>
<option value="csv">CSV</option>
<option value="json">JSON</option>
</select>
</div>

<button
onClick={handleExport}
disabled={prompts.length === 0}
className={`px-4 py-2 rounded-md ${
prompts.length === 0
? 'bg-gray-300 cursor-not-allowed'
: 'bg-blue-500 text-white hover:bg-blue-600'
}`}
>
Export {prompts.length} Prompts
</button>

{exportStatus && (
<div className={`mt-4 p-3 rounded-md ${
exportStatus.includes('Successfully')
? 'bg-green-100 text-green-700'
: 'bg-yellow-100 text-yellow-700'
}`}>
{exportStatus}
</div>
)}
</div>
);
};

export default ExportPrompt;
122 changes: 122 additions & 0 deletions BetterChatGPT/src/components/SystemPromptLibraryMenu/ImportPrompt.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// src/components/SystemPromptLibraryMenu/ImportPrompt.tsx

import React, { useState } from 'react';
import useStore from '@store/store';
import { importPromptCSV } from '@utils/prompt';
import { Prompt } from '@type/prompt';

const ImportPrompt: React.FC = () => {
const setPrompts = useStore(state => state.setPrompts);
const prompts = useStore(state => state.prompts);

const [file, setFile] = useState<File | null>(null);
const [isImporting, setIsImporting] = useState(false);
const [importResult, setImportResult] = useState<{
success: boolean;
count: number;
issues: string[];
} | null>(null);

const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files.length > 0) {
setFile(e.target.files[0]);
setImportResult(null);
}
};

const handleImport = async () => {
if (!file) return;

setIsImporting(true);
setImportResult(null);

try {
const content = await file.text();
const result = importPromptCSV(content);

if (result.prompts.length > 0) {
// Add to existing prompts
const updatedPrompts = [...prompts, ...result.prompts];
setPrompts(updatedPrompts);

setImportResult({
success: true,
count: result.prompts.length,
issues: result.validationIssues
});
} else {
setImportResult({
success: false,
count: 0,
issues: result.validationIssues.length > 0
? result.validationIssues
: ['No valid prompts found in the file']
});
}
} catch (error) {
setImportResult({
success: false,
count: 0,
issues: [`Error importing file: ${error instanceof Error ? error.message : String(error)}`]
});
} finally {
setIsImporting(false);
}
};

return (
<div className="p-4 border rounded-lg bg-white dark:bg-gray-800">
<h2 className="text-xl font-bold mb-4">Import Prompts</h2>

<div className="mb-4">
<label className="block text-sm font-medium mb-1">Select CSV File</label>
<input
type="file"
accept=".csv"
onChange={handleFileChange}
className="w-full border p-2 rounded"
/>
<p className="text-xs text-gray-500 mt-1">
CSV should have columns: name, prompt, temperature
</p>
</div>

<button
onClick={handleImport}
disabled={!file || isImporting}
className={`px-4 py-2 rounded-md ${
!file || isImporting
? 'bg-gray-300 cursor-not-allowed'
: 'bg-blue-500 text-white hover:bg-blue-600'
}`}
>
{isImporting ? 'Importing...' : 'Import Prompts'}
</button>

{importResult && (
<div className={`mt-4 p-3 rounded-md ${
importResult.success ? 'bg-green-100' : 'bg-red-100'
}`}>
<p className={importResult.success ? 'text-green-700' : 'text-red-700'}>
{importResult.success
? `Successfully imported ${importResult.count} prompts`
: 'Import failed'}
</p>

{importResult.issues.length > 0 && (
<div className="mt-2">
<p className="font-medium text-gray-700">Issues:</p>
<ul className="list-disc list-inside text-sm mt-1 max-h-40 overflow-y-auto">
{importResult.issues.map((issue, index) => (
<li key={index} className="text-gray-600">{issue}</li>
))}
</ul>
</div>
)}
</div>
)}
</div>
);
};

export default ImportPrompt;
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// src/components/Prompt/PromptList.tsx

import React from 'react';
import useStore from '@store/store';

const PromptList: React.FC = () => {
const prompts = useStore(state => state.prompts);
const validationErrors = useStore(state => state.promptValidationErrors);

return (
<div className="space-y-4">
<h2 className="text-xl font-bold">Available Prompts</h2>

{prompts.length === 0 ? (
<div className="p-4 bg-gray-100 rounded-lg text-gray-600 text-center">
No prompts available
</div>
) : (
<div className="grid gap-4 md:grid-cols-2">
{prompts.map(prompt => {
const hasErrors = !!validationErrors[prompt.id];

return (
<div
key={prompt.id}
className={`border rounded-lg p-4 ${
hasErrors ? 'border-red-300 bg-red-50' : 'border-gray-200'
}`}
>
<div className="flex justify-between items-start mb-2">
<h3 className="font-medium">{prompt.name}</h3>

{hasErrors && (
<div
className="px-2 py-1 bg-red-100 text-red-800 text-xs rounded-full"
title="This prompt has validation issues that were automatically fixed"
>
Fixed
</div>
)}
</div>

<div className="text-sm text-gray-500 mb-3 line-clamp-2">
{prompt.prompt}
</div>

<div className="flex items-center text-sm">
<div className="mr-4">
<span className="font-medium text-gray-700">Temperature:</span>{' '}
<span className={prompt.temperature < 0 || prompt.temperature > 1 ? 'text-red-600' : ''}>
{prompt.temperature.toFixed(2)}
</span>
</div>

<div>
<span className="font-medium text-gray-700">ID:</span>{' '}
<span className="text-gray-500">{prompt.id.substring(0, 8)}...</span>
</div>
</div>
</div>
);
})}
</div>
)}
</div>
);
};

export default PromptList;
Loading
Loading