Implement pagination for modification history retrieval and update frontend to support paginated data

This commit is contained in:
Dominik Schröter 2025-01-27 13:24:30 +01:00
parent 32cc3d2794
commit 969bacc137
3 changed files with 116 additions and 22 deletions

View file

@ -248,13 +248,34 @@ func (app *App) getDocumentHandler() gin.HandlerFunc {
// Section for local-db actions // Section for local-db actions
func (app *App) getModificationHistoryHandler(c *gin.Context) { func (app *App) getModificationHistoryHandler(c *gin.Context) {
modifications, err := GetAllModifications(app.Database) // Parse pagination parameters
page := 1
pageSize := 20
if p, err := strconv.Atoi(c.DefaultQuery("page", "1")); err == nil && p > 0 {
page = p
}
if ps, err := strconv.Atoi(c.DefaultQuery("pageSize", "20")); err == nil && ps > 0 && ps <= 100 {
pageSize = ps
}
// Get paginated modifications and total count
modifications, total, err := GetPaginatedModifications(app.Database, page, pageSize)
if err != nil { if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve modification history"}) c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve modification history"})
log.Errorf("Failed to retrieve modification history: %v", err) log.Errorf("Failed to retrieve modification history: %v", err)
return return
} }
c.JSON(http.StatusOK, modifications)
totalPages := (int(total) + pageSize - 1) / pageSize
c.JSON(http.StatusOK, gin.H{
"items": modifications,
"totalItems": total,
"totalPages": totalPages,
"currentPage": page,
"pageSize": pageSize,
})
} }
func (app *App) undoModificationHandler(c *gin.Context) { func (app *App) undoModificationHandler(c *gin.Context) {

View file

@ -63,13 +63,35 @@ func GetModification(db *gorm.DB, id uint) (*ModificationHistory, error) {
return &record, result.Error return &record, result.Error
} }
// GetAllModifications retrieves all modification records from the database // GetAllModifications retrieves all modification records from the database (deprecated - use GetPaginatedModifications instead)
func GetAllModifications(db *gorm.DB) ([]ModificationHistory, error) { func GetAllModifications(db *gorm.DB) ([]ModificationHistory, error) {
var records []ModificationHistory var records []ModificationHistory
result := db.Order("date_changed DESC").Find(&records) // GORM's Find method retrieves all records result := db.Order("date_changed DESC").Find(&records)
return records, result.Error return records, result.Error
} }
// GetPaginatedModifications retrieves a page of modification records with total count
func GetPaginatedModifications(db *gorm.DB, page int, pageSize int) ([]ModificationHistory, int64, error) {
var records []ModificationHistory
var total int64
// Get total count
if err := db.Model(&ModificationHistory{}).Count(&total).Error; err != nil {
return nil, 0, err
}
// Calculate offset
offset := (page - 1) * pageSize
// Get paginated records
result := db.Order("date_changed DESC").
Offset(offset).
Limit(pageSize).
Find(&records)
return records, total, result.Error
}
// UndoModification marks a modification record as undone and sets the undo date // UndoModification marks a modification record as undone and sets the undo date
func SetModificationUndone(db *gorm.DB, record *ModificationHistory) error { func SetModificationUndone(db *gorm.DB, record *ModificationHistory) error {
record.Undone = true record.Undone = true

View file

@ -12,11 +12,23 @@ interface ModificationHistory {
UndoneDate: string | null; UndoneDate: string | null;
} }
interface PaginatedResponse {
items: ModificationHistory[];
totalItems: number;
totalPages: number;
currentPage: number;
pageSize: number;
}
const History: React.FC = () => { const History: React.FC = () => {
const [modifications, setModifications] = useState<ModificationHistory[]>([]); const [modifications, setModifications] = useState<ModificationHistory[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [paperlessUrl, setPaperlessUrl] = useState<string>(''); const [paperlessUrl, setPaperlessUrl] = useState<string>('');
const [currentPage, setCurrentPage] = useState(1);
const [totalPages, setTotalPages] = useState(1);
const [totalItems, setTotalItems] = useState(0);
const pageSize = 20;
// Get Paperless URL // Get Paperless URL
useEffect(() => { useEffect(() => {
@ -36,19 +48,22 @@ const History: React.FC = () => {
fetchUrl(); fetchUrl();
}, []); }, []);
// Get all modifications // Get modifications with pagination
useEffect(() => { useEffect(() => {
fetchModifications(); fetchModifications(currentPage);
}, []); }, [currentPage]);
const fetchModifications = async () => { const fetchModifications = async (page: number) => {
setLoading(true);
try { try {
const response = await fetch('/api/modifications'); const response = await fetch(`/api/modifications?page=${page}&pageSize=${pageSize}`);
if (!response.ok) { if (!response.ok) {
throw new Error('Failed to fetch modifications'); throw new Error('Failed to fetch modifications');
} }
const data = await response.json(); const data: PaginatedResponse = await response.json();
setModifications(data); setModifications(data.items);
setTotalPages(data.totalPages);
setTotalItems(data.totalItems);
} catch (err) { } catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error occurred'); setError(err instanceof Error ? err.message : 'Unknown error occurred');
} finally { } finally {
@ -108,19 +123,55 @@ const History: React.FC = () => {
No modifications found No modifications found
</p> </p>
) : ( ) : (
<div className="grid gap-4 md:grid-cols-1 lg:grid-cols-1"> <>
{modifications.map((modification) => ( <div className="grid gap-4 md:grid-cols-1 lg:grid-cols-1 mb-6">
<UndoCard {modifications.map((modification) => (
key={modification.ID} <UndoCard
{...modification} key={modification.ID}
onUndo={handleUndo} {...modification}
paperlessUrl={paperlessUrl} onUndo={handleUndo}
/> paperlessUrl={paperlessUrl}
))} />
</div> ))}
</div>
<div className="flex items-center justify-between border-t border-gray-200 dark:border-gray-700 pt-4">
<div className="flex items-center text-sm text-gray-500 dark:text-gray-400">
<span>
Showing {((currentPage - 1) * pageSize) + 1} to {Math.min(currentPage * pageSize, totalItems)} of {totalItems} results
</span>
</div>
<div className="flex items-center space-x-2">
<button
onClick={() => setCurrentPage(page => Math.max(1, page - 1))}
disabled={currentPage === 1}
className={`px-3 py-1 rounded-md ${
currentPage === 1
? 'bg-gray-100 text-gray-400 cursor-not-allowed dark:bg-gray-800'
: 'bg-blue-500 text-white hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700'
}`}
>
Previous
</button>
<span className="text-sm text-gray-600 dark:text-gray-300">
Page {currentPage} of {totalPages}
</span>
<button
onClick={() => setCurrentPage(page => Math.min(totalPages, page + 1))}
disabled={currentPage === totalPages}
className={`px-3 py-1 rounded-md ${
currentPage === totalPages
? 'bg-gray-100 text-gray-400 cursor-not-allowed dark:bg-gray-800'
: 'bg-blue-500 text-white hover:bg-blue-600 dark:bg-blue-600 dark:hover:bg-blue-700'
}`}
>
Next
</button>
</div>
</div>
</>
)} )}
</div> </div>
); );
}; };
export default History; export default History;