mirror of
https://github.com/icereed/paperless-gpt.git
synced 2025-03-13 05:08:01 -05:00
feat: add correspondent management and update related components
This commit is contained in:
parent
066ed0bb72
commit
91da6eff80
11 changed files with 120 additions and 20 deletions
11
app_llm.go
11
app_llm.go
|
@ -6,6 +6,7 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -61,6 +62,7 @@ func (app *App) getSuggestedTags(
|
||||||
content string,
|
content string,
|
||||||
suggestedTitle string,
|
suggestedTitle string,
|
||||||
availableTags []string,
|
availableTags []string,
|
||||||
|
originalTags []string,
|
||||||
logger *logrus.Entry) ([]string, error) {
|
logger *logrus.Entry) ([]string, error) {
|
||||||
likelyLanguage := getLikelyLanguage()
|
likelyLanguage := getLikelyLanguage()
|
||||||
|
|
||||||
|
@ -71,6 +73,7 @@ func (app *App) getSuggestedTags(
|
||||||
err := tagTemplate.Execute(&promptBuffer, map[string]interface{}{
|
err := tagTemplate.Execute(&promptBuffer, map[string]interface{}{
|
||||||
"Language": likelyLanguage,
|
"Language": likelyLanguage,
|
||||||
"AvailableTags": availableTags,
|
"AvailableTags": availableTags,
|
||||||
|
"OriginalTags": originalTags,
|
||||||
"Title": suggestedTitle,
|
"Title": suggestedTitle,
|
||||||
"Content": content,
|
"Content": content,
|
||||||
})
|
})
|
||||||
|
@ -103,6 +106,12 @@ func (app *App) getSuggestedTags(
|
||||||
suggestedTags[i] = strings.TrimSpace(tag)
|
suggestedTags[i] = strings.TrimSpace(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// append the original tags to the suggested tags
|
||||||
|
suggestedTags = append(suggestedTags, originalTags...)
|
||||||
|
// Remove duplicates
|
||||||
|
slices.Sort(suggestedTags)
|
||||||
|
suggestedTags = slices.Compact(suggestedTags)
|
||||||
|
|
||||||
// Filter out tags that are not in the available tags list
|
// Filter out tags that are not in the available tags list
|
||||||
filteredTags := []string{}
|
filteredTags := []string{}
|
||||||
for _, tag := range suggestedTags {
|
for _, tag := range suggestedTags {
|
||||||
|
@ -278,7 +287,7 @@ func (app *App) generateDocumentSuggestions(ctx context.Context, suggestionReque
|
||||||
}
|
}
|
||||||
|
|
||||||
if suggestionRequest.GenerateTags {
|
if suggestionRequest.GenerateTags {
|
||||||
suggestedTags, err = app.getSuggestedTags(ctx, content, suggestedTitle, availableTagNames, docLogger)
|
suggestedTags, err = app.getSuggestedTags(ctx, content, suggestedTitle, availableTagNames, doc.Tags, docLogger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
errorsList = append(errorsList, fmt.Errorf("Document %d: %v", documentID, err))
|
errorsList = append(errorsList, fmt.Errorf("Document %d: %v", documentID, err))
|
||||||
|
|
50
paperless.go
50
paperless.go
|
@ -163,6 +163,11 @@ func (client *PaperlessClient) GetDocumentsByTags(ctx context.Context, tags []st
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allCorrespondents, err := client.GetAllCorrespondents(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
documents := make([]Document, 0, len(documentsResponse.Results))
|
documents := make([]Document, 0, len(documentsResponse.Results))
|
||||||
for _, result := range documentsResponse.Results {
|
for _, result := range documentsResponse.Results {
|
||||||
tagNames := make([]string, len(result.Tags))
|
tagNames := make([]string, len(result.Tags))
|
||||||
|
@ -175,11 +180,22 @@ func (client *PaperlessClient) GetDocumentsByTags(ctx context.Context, tags []st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
correspondentName := ""
|
||||||
|
if result.Correspondent != 0 {
|
||||||
|
for name, id := range allCorrespondents {
|
||||||
|
if result.Correspondent == id {
|
||||||
|
correspondentName = name
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
documents = append(documents, Document{
|
documents = append(documents, Document{
|
||||||
ID: result.ID,
|
ID: result.ID,
|
||||||
Title: result.Title,
|
Title: result.Title,
|
||||||
Content: result.Content,
|
Content: result.Content,
|
||||||
Tags: tagNames,
|
Correspondent: correspondentName,
|
||||||
|
Tags: tagNames,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,6 +243,12 @@ func (client *PaperlessClient) GetDocument(ctx context.Context, documentID int)
|
||||||
return Document{}, err
|
return Document{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allCorrespondents, err := client.GetAllCorrespondents(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return Document{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match tag IDs to tag names
|
||||||
tagNames := make([]string, len(documentResponse.Tags))
|
tagNames := make([]string, len(documentResponse.Tags))
|
||||||
for i, resultTagID := range documentResponse.Tags {
|
for i, resultTagID := range documentResponse.Tags {
|
||||||
for tagName, tagID := range allTags {
|
for tagName, tagID := range allTags {
|
||||||
|
@ -237,11 +259,21 @@ func (client *PaperlessClient) GetDocument(ctx context.Context, documentID int)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Match correspondent ID to correspondent name
|
||||||
|
correspondentName := ""
|
||||||
|
for name, id := range allCorrespondents {
|
||||||
|
if documentResponse.Correspondent == id {
|
||||||
|
correspondentName = name
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Document{
|
return Document{
|
||||||
ID: documentResponse.ID,
|
ID: documentResponse.ID,
|
||||||
Title: documentResponse.Title,
|
Title: documentResponse.Title,
|
||||||
Content: documentResponse.Content,
|
Content: documentResponse.Content,
|
||||||
Tags: tagNames,
|
Correspondent: correspondentName,
|
||||||
|
Tags: tagNames,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,8 +334,6 @@ func (client *PaperlessClient) UpdateDocuments(ctx context.Context, documents []
|
||||||
// remove autoTag to prevent infinite loop - this is required in case of undo
|
// remove autoTag to prevent infinite loop - this is required in case of undo
|
||||||
tags = removeTagFromList(tags, autoTag)
|
tags = removeTagFromList(tags, autoTag)
|
||||||
|
|
||||||
// keep previous tags
|
|
||||||
tags = append(tags, originalTags...)
|
|
||||||
// remove duplicates
|
// remove duplicates
|
||||||
slices.Sort(tags)
|
slices.Sort(tags)
|
||||||
tags = slices.Compact(tags)
|
tags = slices.Compact(tags)
|
||||||
|
|
|
@ -348,7 +348,8 @@ func TestUpdateDocuments(t *testing.T) {
|
||||||
// Expected updated fields
|
// Expected updated fields
|
||||||
expectedFields := map[string]interface{}{
|
expectedFields := map[string]interface{}{
|
||||||
"title": "New Title",
|
"title": "New Title",
|
||||||
"tags": []interface{}{float64(idTag1), float64(idTag2), float64(idTag3)}, // keep also previous tags
|
// do not keep previous tags since the tag generation will already take care to include old ones:
|
||||||
|
"tags": []interface{}{float64(idTag2), float64(idTag3)},
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, expectedFields, updatedFields)
|
assert.Equal(t, expectedFields, updatedFields)
|
||||||
|
|
13
types.go
13
types.go
|
@ -11,7 +11,7 @@ type GetDocumentsApiResponse struct {
|
||||||
All []int `json:"all"`
|
All []int `json:"all"`
|
||||||
Results []struct {
|
Results []struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Correspondent interface{} `json:"correspondent"`
|
Correspondent int `json:"correspondent"`
|
||||||
DocumentType interface{} `json:"document_type"`
|
DocumentType interface{} `json:"document_type"`
|
||||||
StoragePath interface{} `json:"storage_path"`
|
StoragePath interface{} `json:"storage_path"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
|
@ -38,7 +38,7 @@ type GetDocumentsApiResponse struct {
|
||||||
|
|
||||||
type GetDocumentApiResponse struct {
|
type GetDocumentApiResponse struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Correspondent interface{} `json:"correspondent"`
|
Correspondent int `json:"correspondent"`
|
||||||
DocumentType interface{} `json:"document_type"`
|
DocumentType interface{} `json:"document_type"`
|
||||||
StoragePath interface{} `json:"storage_path"`
|
StoragePath interface{} `json:"storage_path"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
|
@ -59,10 +59,11 @@ type GetDocumentApiResponse struct {
|
||||||
// Document is a stripped down version of the document object from paperless-ngx.
|
// Document is a stripped down version of the document object from paperless-ngx.
|
||||||
// Response payload for /documents endpoint and part of request payload for /generate-suggestions endpoint
|
// Response payload for /documents endpoint and part of request payload for /generate-suggestions endpoint
|
||||||
type Document struct {
|
type Document struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Tags []string `json:"tags"`
|
Tags []string `json:"tags"`
|
||||||
|
Correspondent string `json:"correspondent"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateSuggestionsRequest is the request payload for generating suggestions for /generate-suggestions endpoint
|
// GenerateSuggestionsRequest is the request payload for generating suggestions for /generate-suggestions endpoint
|
||||||
|
|
|
@ -11,12 +11,14 @@ export interface Document {
|
||||||
title: string;
|
title: string;
|
||||||
content: string;
|
content: string;
|
||||||
tags: string[];
|
tags: string[];
|
||||||
|
correspondent: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GenerateSuggestionsRequest {
|
export interface GenerateSuggestionsRequest {
|
||||||
documents: Document[];
|
documents: Document[];
|
||||||
generate_titles?: boolean;
|
generate_titles?: boolean;
|
||||||
generate_tags?: boolean;
|
generate_tags?: boolean;
|
||||||
|
generate_correspondents?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DocumentSuggestion {
|
export interface DocumentSuggestion {
|
||||||
|
@ -25,6 +27,7 @@ export interface DocumentSuggestion {
|
||||||
suggested_title?: string;
|
suggested_title?: string;
|
||||||
suggested_tags?: string[];
|
suggested_tags?: string[];
|
||||||
suggested_content?: string;
|
suggested_content?: string;
|
||||||
|
suggested_correspondent?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TagOption {
|
export interface TagOption {
|
||||||
|
@ -43,6 +46,7 @@ const DocumentProcessor: React.FC = () => {
|
||||||
const [filterTag, setFilterTag] = useState<string | null>(null);
|
const [filterTag, setFilterTag] = useState<string | null>(null);
|
||||||
const [generateTitles, setGenerateTitles] = useState(true);
|
const [generateTitles, setGenerateTitles] = useState(true);
|
||||||
const [generateTags, setGenerateTags] = useState(true);
|
const [generateTags, setGenerateTags] = useState(true);
|
||||||
|
const [generateCorrespondents, setGenerateCorrespondents] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
// Custom hook to fetch initial data
|
// Custom hook to fetch initial data
|
||||||
|
@ -81,6 +85,7 @@ const DocumentProcessor: React.FC = () => {
|
||||||
documents,
|
documents,
|
||||||
generate_titles: generateTitles,
|
generate_titles: generateTitles,
|
||||||
generate_tags: generateTags,
|
generate_tags: generateTags,
|
||||||
|
generate_correspondents: generateCorrespondents,
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data } = await axios.post<DocumentSuggestion[]>(
|
const { data } = await axios.post<DocumentSuggestion[]>(
|
||||||
|
@ -137,6 +142,7 @@ const DocumentProcessor: React.FC = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleTitleChange = (docId: number, title: string) => {
|
const handleTitleChange = (docId: number, title: string) => {
|
||||||
setSuggestions((prevSuggestions) =>
|
setSuggestions((prevSuggestions) =>
|
||||||
prevSuggestions.map((doc) =>
|
prevSuggestions.map((doc) =>
|
||||||
|
@ -145,6 +151,14 @@ const DocumentProcessor: React.FC = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCorrespondentChange = (docId: number, correspondent: string) => {
|
||||||
|
setSuggestions((prevSuggestions) =>
|
||||||
|
prevSuggestions.map((doc) =>
|
||||||
|
doc.id === docId ? { ...doc, suggested_correspondent: correspondent } : doc
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const resetSuggestions = () => {
|
const resetSuggestions = () => {
|
||||||
setSuggestions([]);
|
setSuggestions([]);
|
||||||
};
|
};
|
||||||
|
@ -214,6 +228,8 @@ const DocumentProcessor: React.FC = () => {
|
||||||
setGenerateTitles={setGenerateTitles}
|
setGenerateTitles={setGenerateTitles}
|
||||||
generateTags={generateTags}
|
generateTags={generateTags}
|
||||||
setGenerateTags={setGenerateTags}
|
setGenerateTags={setGenerateTags}
|
||||||
|
generateCorrespondents={generateCorrespondents}
|
||||||
|
setGenerateCorrespondents={setGenerateCorrespondents}
|
||||||
onProcess={handleProcessDocuments}
|
onProcess={handleProcessDocuments}
|
||||||
processing={processing}
|
processing={processing}
|
||||||
onReload={reloadDocuments}
|
onReload={reloadDocuments}
|
||||||
|
@ -225,6 +241,7 @@ const DocumentProcessor: React.FC = () => {
|
||||||
onTitleChange={handleTitleChange}
|
onTitleChange={handleTitleChange}
|
||||||
onTagAddition={handleTagAddition}
|
onTagAddition={handleTagAddition}
|
||||||
onTagDeletion={handleTagDeletion}
|
onTagDeletion={handleTagDeletion}
|
||||||
|
onCorrespondentChange={handleCorrespondentChange}
|
||||||
onBack={resetSuggestions}
|
onBack={resetSuggestions}
|
||||||
onUpdate={handleUpdateDocuments}
|
onUpdate={handleUpdateDocuments}
|
||||||
updating={updating}
|
updating={updating}
|
||||||
|
|
|
@ -13,6 +13,9 @@ const DocumentCard: React.FC<DocumentCardProps> = ({ document }) => (
|
||||||
? `${document.content.substring(0, 100)}...`
|
? `${document.content.substring(0, 100)}...`
|
||||||
: document.content}
|
: document.content}
|
||||||
</p>
|
</p>
|
||||||
|
<p className="text-sm text-gray-600 dark:text-gray-400 mt-2">
|
||||||
|
Correspondent: <span className="font-bold text-blue-600 dark:text-blue-400">{document.correspondent}</span>
|
||||||
|
</p>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
{document.tags.map((tag) => (
|
{document.tags.map((tag) => (
|
||||||
<span
|
<span
|
||||||
|
@ -27,6 +30,9 @@ const DocumentCard: React.FC<DocumentCardProps> = ({ document }) => (
|
||||||
<div className="text-sm text-white p-2 bg-gray-800 dark:bg-gray-900 rounded-md w-full max-h-full overflow-y-auto">
|
<div className="text-sm text-white p-2 bg-gray-800 dark:bg-gray-900 rounded-md w-full max-h-full overflow-y-auto">
|
||||||
<h3 className="text-lg font-semibold text-white">{document.title}</h3>
|
<h3 className="text-lg font-semibold text-white">{document.title}</h3>
|
||||||
<p className="mt-2 whitespace-pre-wrap">{document.content}</p>
|
<p className="mt-2 whitespace-pre-wrap">{document.content}</p>
|
||||||
|
<p className="mt-2">
|
||||||
|
Correspondent: <span className="font-bold text-blue-400">{document.correspondent}</span>
|
||||||
|
</p>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
{document.tags.map((tag) => (
|
{document.tags.map((tag) => (
|
||||||
<span
|
<span
|
||||||
|
|
|
@ -9,6 +9,8 @@ interface DocumentsToProcessProps {
|
||||||
setGenerateTitles: React.Dispatch<React.SetStateAction<boolean>>;
|
setGenerateTitles: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
generateTags: boolean;
|
generateTags: boolean;
|
||||||
setGenerateTags: React.Dispatch<React.SetStateAction<boolean>>;
|
setGenerateTags: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
generateCorrespondents: boolean;
|
||||||
|
setGenerateCorrespondents: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
onProcess: () => void;
|
onProcess: () => void;
|
||||||
processing: boolean;
|
processing: boolean;
|
||||||
onReload: () => void;
|
onReload: () => void;
|
||||||
|
@ -20,6 +22,8 @@ const DocumentsToProcess: React.FC<DocumentsToProcessProps> = ({
|
||||||
setGenerateTitles,
|
setGenerateTitles,
|
||||||
generateTags,
|
generateTags,
|
||||||
setGenerateTags,
|
setGenerateTags,
|
||||||
|
generateCorrespondents,
|
||||||
|
setGenerateCorrespondents,
|
||||||
onProcess,
|
onProcess,
|
||||||
processing,
|
processing,
|
||||||
onReload,
|
onReload,
|
||||||
|
@ -64,6 +68,15 @@ const DocumentsToProcess: React.FC<DocumentsToProcessProps> = ({
|
||||||
/>
|
/>
|
||||||
<span className="text-gray-700 dark:text-gray-200">Generate Tags</span>
|
<span className="text-gray-700 dark:text-gray-200">Generate Tags</span>
|
||||||
</label>
|
</label>
|
||||||
|
<label className="flex items-center space-x-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={generateCorrespondents}
|
||||||
|
onChange={(e) => setGenerateCorrespondents(e.target.checked)}
|
||||||
|
className="dark:bg-gray-700 dark:border-gray-600"
|
||||||
|
/>
|
||||||
|
<span className="text-gray-700 dark:text-gray-200">Generate Correspondents</span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
|
|
@ -8,6 +8,7 @@ interface SuggestionCardProps {
|
||||||
onTitleChange: (docId: number, title: string) => void;
|
onTitleChange: (docId: number, title: string) => void;
|
||||||
onTagAddition: (docId: number, tag: TagOption) => void;
|
onTagAddition: (docId: number, tag: TagOption) => void;
|
||||||
onTagDeletion: (docId: number, index: number) => void;
|
onTagDeletion: (docId: number, index: number) => void;
|
||||||
|
onCorrespondentChange: (docId: number, correspondent: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SuggestionCard: React.FC<SuggestionCardProps> = ({
|
const SuggestionCard: React.FC<SuggestionCardProps> = ({
|
||||||
|
@ -16,6 +17,7 @@ const SuggestionCard: React.FC<SuggestionCardProps> = ({
|
||||||
onTitleChange,
|
onTitleChange,
|
||||||
onTagAddition,
|
onTagAddition,
|
||||||
onTagDeletion,
|
onTagDeletion,
|
||||||
|
onCorrespondentChange,
|
||||||
}) => {
|
}) => {
|
||||||
const sortedAvailableTags = availableTags.sort((a, b) => a.name.localeCompare(b.name));
|
const sortedAvailableTags = availableTags.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
const document = suggestion.original_document;
|
const document = suggestion.original_document;
|
||||||
|
@ -49,6 +51,9 @@ const SuggestionCard: React.FC<SuggestionCardProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
|
Suggested Title
|
||||||
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={suggestion.suggested_title || ""}
|
value={suggestion.suggested_title || ""}
|
||||||
|
@ -56,6 +61,9 @@ const SuggestionCard: React.FC<SuggestionCardProps> = ({
|
||||||
className="w-full border border-gray-300 dark:border-gray-600 rounded px-2 py-1 mt-2 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-gray-200"
|
className="w-full border border-gray-300 dark:border-gray-600 rounded px-2 py-1 mt-2 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-gray-200"
|
||||||
/>
|
/>
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
|
Suggested Tags
|
||||||
|
</label>
|
||||||
<ReactTags
|
<ReactTags
|
||||||
selected={
|
selected={
|
||||||
suggestion.suggested_tags?.map((tag, index) => ({
|
suggestion.suggested_tags?.map((tag, index) => ({
|
||||||
|
@ -99,6 +107,18 @@ const SuggestionCard: React.FC<SuggestionCardProps> = ({
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="mt-4">
|
||||||
|
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||||
|
Suggested Correspondent
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={suggestion.suggested_correspondent || ""}
|
||||||
|
onChange={(e) => onCorrespondentChange(suggestion.id, e.target.value)}
|
||||||
|
className="w-full border border-gray-300 dark:border-gray-600 rounded px-2 py-1 mt-2 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-gray-200"
|
||||||
|
placeholder="Correspondent"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,6 +8,7 @@ interface SuggestionsReviewProps {
|
||||||
onTitleChange: (docId: number, title: string) => void;
|
onTitleChange: (docId: number, title: string) => void;
|
||||||
onTagAddition: (docId: number, tag: TagOption) => void;
|
onTagAddition: (docId: number, tag: TagOption) => void;
|
||||||
onTagDeletion: (docId: number, index: number) => void;
|
onTagDeletion: (docId: number, index: number) => void;
|
||||||
|
onCorrespondentChange: (docId: number, correspondent: string) => void;
|
||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
onUpdate: () => void;
|
onUpdate: () => void;
|
||||||
updating: boolean;
|
updating: boolean;
|
||||||
|
@ -19,6 +20,7 @@ const SuggestionsReview: React.FC<SuggestionsReviewProps> = ({
|
||||||
onTitleChange,
|
onTitleChange,
|
||||||
onTagAddition,
|
onTagAddition,
|
||||||
onTagDeletion,
|
onTagDeletion,
|
||||||
|
onCorrespondentChange,
|
||||||
onBack,
|
onBack,
|
||||||
onUpdate,
|
onUpdate,
|
||||||
updating,
|
updating,
|
||||||
|
@ -36,6 +38,7 @@ const SuggestionsReview: React.FC<SuggestionsReviewProps> = ({
|
||||||
onTitleChange={onTitleChange}
|
onTitleChange={onTitleChange}
|
||||||
onTagAddition={onTagAddition}
|
onTagAddition={onTagAddition}
|
||||||
onTagDeletion={onTagDeletion}
|
onTagDeletion={onTagDeletion}
|
||||||
|
onCorrespondentChange={onCorrespondentChange}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"root":["./src/app.tsx","./src/documentprocessor.tsx","./src/experimentalocr.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/documentcard.tsx","./src/components/documentstoprocess.tsx","./src/components/nodocuments.tsx","./src/components/successmodal.tsx","./src/components/suggestioncard.tsx","./src/components/suggestionsreview.tsx"],"version":"5.6.2"}
|
{"root":["./src/app.tsx","./src/documentprocessor.tsx","./src/experimentalocr.tsx","./src/history.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/components/documentcard.tsx","./src/components/documentstoprocess.tsx","./src/components/nodocuments.tsx","./src/components/sidebar.tsx","./src/components/successmodal.tsx","./src/components/suggestioncard.tsx","./src/components/suggestionsreview.tsx","./src/components/undocard.tsx"],"version":"5.7.2"}
|
|
@ -1 +1 @@
|
||||||
{"root":["./vite.config.ts"],"version":"5.6.2"}
|
{"root":["./vite.config.ts"],"version":"5.7.2"}
|
Loading…
Reference in a new issue