Skip to content

Commit d18f401

Browse files
committed
feat: add document metadata management system
- Add documents with metadata (title, URL, checksum, description) - Add metadata UI section in document details - Replace JavaScript alerts/confirms with modern modal dialogs - Make email language dynamic based on user's interface language
1 parent 6e12fb6 commit d18f401

File tree

7 files changed

+379
-35
lines changed

7 files changed

+379
-35
lines changed

internal/application/services/reminder.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func (s *ReminderService) SendReminders(
4040
sentBy string,
4141
specificEmails []string,
4242
docURL string,
43+
locale string,
4344
) (*models.ReminderSendResult, error) {
4445

4546
allSigners, err := s.expectedSignerRepo.ListWithStatusByDocID(ctx, docID)
@@ -73,7 +74,7 @@ func (s *ReminderService) SendReminders(
7374
}
7475

7576
for _, signer := range pendingSigners {
76-
err := s.sendSingleReminder(ctx, docID, signer.Email, signer.Name, sentBy, docURL)
77+
err := s.sendSingleReminder(ctx, docID, signer.Email, signer.Name, sentBy, docURL, locale)
7778
if err != nil {
7879
result.Failed++
7980
result.Errors = append(result.Errors, fmt.Sprintf("%s: %v", signer.Email, err))
@@ -93,6 +94,7 @@ func (s *ReminderService) sendSingleReminder(
9394
recipientName string,
9495
sentBy string,
9596
docURL string,
97+
locale string,
9698
) error {
9799

98100
signURL := fmt.Sprintf("%s/sign?doc=%s", s.baseURL, docID)
@@ -106,7 +108,7 @@ func (s *ReminderService) sendSingleReminder(
106108
Status: "sent",
107109
}
108110

109-
err := email.SendSignatureReminderEmail(ctx, s.emailSender, []string{recipientEmail}, "fr", docID, docURL, signURL, recipientName)
111+
err := email.SendSignatureReminderEmail(ctx, s.emailSender, []string{recipientEmail}, locale, docID, docURL, signURL, recipientName)
110112
if err != nil {
111113
log.Status = "failed"
112114
errMsg := err.Error()

internal/infrastructure/database/admin_repository.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ func (r *AdminRepository) ListDocumentsWithCounts(ctx context.Context) ([]Docume
3939
SELECT DISTINCT doc_id FROM signatures
4040
UNION
4141
SELECT DISTINCT doc_id FROM expected_signers
42+
UNION
43+
SELECT DISTINCT doc_id FROM documents
4244
) AS all_docs
4345
LEFT JOIN (
4446
SELECT doc_id, COUNT(*) as sig_count

internal/presentation/admin/handlers_expected_signers.go

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ import (
2020
const maxTextareaSize = 10000
2121

2222
type reminderService interface {
23-
SendReminders(ctx context.Context, docID, sentBy string, specificEmails []string, docURL string) (*models.ReminderSendResult, error)
23+
SendReminders(ctx context.Context, docID, sentBy string, specificEmails []string, docURL string, locale string) (*models.ReminderSendResult, error)
2424
GetReminderStats(ctx context.Context, docID string) (*models.ReminderStats, error)
2525
GetReminderHistory(ctx context.Context, docID string) ([]*models.ReminderLog, error)
2626
}
2727

2828
type ExpectedSignersHandlers struct {
2929
expectedRepo *database.ExpectedSignerRepository
3030
adminRepo *database.AdminRepository
31+
documentRepo *database.DocumentRepository
3132
userService userService
3233
reminderService reminderService
3334
templates *template.Template
@@ -37,6 +38,7 @@ type ExpectedSignersHandlers struct {
3738
func NewExpectedSignersHandlers(
3839
expectedRepo *database.ExpectedSignerRepository,
3940
adminRepo *database.AdminRepository,
41+
documentRepo *database.DocumentRepository,
4042
userService userService,
4143
reminderService reminderService,
4244
templates *template.Template,
@@ -45,6 +47,7 @@ func NewExpectedSignersHandlers(
4547
return &ExpectedSignersHandlers{
4648
expectedRepo: expectedRepo,
4749
adminRepo: adminRepo,
50+
documentRepo: documentRepo,
4851
userService: userService,
4952
reminderService: reminderService,
5053
templates: templates,
@@ -121,6 +124,16 @@ func (h *ExpectedSignersHandlers) HandleDocumentDetailsWithExpected(w http.Respo
121124
}
122125
}
123126

127+
// Get document metadata
128+
var documentMetadata *models.Document
129+
if h.documentRepo != nil {
130+
documentMetadata, err = h.documentRepo.GetByDocID(ctx, docID)
131+
if err != nil {
132+
logger.Logger.Error("Failed to retrieve document metadata", "error", err.Error())
133+
documentMetadata = nil
134+
}
135+
}
136+
124137
// Find unexpected signatures (signed but not in expected list)
125138
unexpectedSignatures := []*models.Signature{}
126139
if len(expectedSigners) > 0 {
@@ -141,6 +154,7 @@ func (h *ExpectedSignersHandlers) HandleDocumentDetailsWithExpected(w http.Respo
141154
User *models.User
142155
BaseURL string
143156
DocID *string
157+
Document *models.Document
144158
Signatures []*models.Signature
145159
ExpectedSigners []*models.ExpectedSignerWithStatus
146160
Stats *models.DocCompletionStats
@@ -156,6 +170,7 @@ func (h *ExpectedSignersHandlers) HandleDocumentDetailsWithExpected(w http.Respo
156170
User: user,
157171
BaseURL: h.baseURL,
158172
DocID: &docID,
173+
Document: documentMetadata,
159174
Signatures: signatures,
160175
ExpectedSigners: expectedSigners,
161176
Stats: stats,
@@ -395,7 +410,6 @@ func (h *ExpectedSignersHandlers) HandleSendReminders(w http.ResponseWriter, r *
395410
}
396411

397412
sendMode := r.FormValue("send_mode")
398-
docURL := r.FormValue("doc_url")
399413
var selectedEmails []string
400414

401415
if sendMode == "selected" {
@@ -406,7 +420,22 @@ func (h *ExpectedSignersHandlers) HandleSendReminders(w http.ResponseWriter, r *
406420
}
407421
}
408422

409-
result, err := h.reminderService.SendReminders(ctx, docID, user.Email, selectedEmails, docURL)
423+
// Get document URL from metadata
424+
var docURL string
425+
if h.documentRepo != nil {
426+
doc, err := h.documentRepo.GetByDocID(ctx, docID)
427+
if err != nil {
428+
logger.Logger.Error("Failed to get document metadata for reminder", "error", err.Error(), "doc_id", docID)
429+
}
430+
if doc != nil && doc.URL != "" {
431+
docURL = doc.URL
432+
}
433+
}
434+
435+
// Get language from context
436+
locale := i18n.GetLang(ctx)
437+
438+
result, err := h.reminderService.SendReminders(ctx, docID, user.Email, selectedEmails, docURL, locale)
410439
if err != nil {
411440
logger.Logger.Error("Failed to send reminders", "error", err.Error())
412441
http.Error(w, "Failed to send reminders", http.StatusInternalServerError)

internal/presentation/admin/routes_admin.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func RegisterAdminRoutes(cfg *config.Config, templates *template.Template, db *s
2020
adminRepo := database.NewAdminRepository(db)
2121
expectedSignerRepo := database.NewExpectedSignerRepository(db)
2222
reminderRepo := database.NewReminderRepository(db)
23+
documentRepo := database.NewDocumentRepository(db)
2324

2425
// Initialize reminder service if email sender is available
2526
var reminderService reminderService
@@ -37,7 +38,8 @@ func RegisterAdminRoutes(cfg *config.Config, templates *template.Template, db *s
3738
// Initialize middleware and handlers
3839
adminMiddleware := NewAdminMiddleware(authService, cfg.App.BaseURL, cfg.App.AdminEmails, templates)
3940
adminHandlers := NewAdminHandlers(adminRepo, authService, templates, cfg.App.BaseURL)
40-
expectedHandlers := NewExpectedSignersHandlers(expectedSignerRepo, adminRepo, authService, reminderService, templates, cfg.App.BaseURL)
41+
expectedHandlers := NewExpectedSignersHandlers(expectedSignerRepo, adminRepo, documentRepo, authService, reminderService, templates, cfg.App.BaseURL)
42+
documentHandlers := NewDocumentHandlers(documentRepo, authService)
4143

4244
// Register admin routes
4345
r.Get("/admin", adminMiddleware.RequireAdmin(adminHandlers.HandleDashboard))
@@ -48,5 +50,10 @@ func RegisterAdminRoutes(cfg *config.Config, templates *template.Template, db *s
4850
r.Get("/admin/docs/{docID}/reminders/history", adminMiddleware.RequireAdmin(expectedHandlers.HandleGetReminderHistory))
4951
r.Get("/admin/docs/{docID}/status.json", adminMiddleware.RequireAdmin(expectedHandlers.HandleGetDocumentStatusJSON))
5052
r.Get("/admin/api/chain-integrity/{docID}", adminMiddleware.RequireAdmin(adminHandlers.HandleChainIntegrityAPI))
53+
54+
// Document metadata routes
55+
r.Get("/admin/docs/{docID}/metadata", adminMiddleware.RequireAdmin(documentHandlers.HandleGetDocumentMetadata))
56+
r.Post("/admin/docs/{docID}/metadata", adminMiddleware.RequireAdmin(documentHandlers.HandleUpdateDocumentMetadata))
57+
r.Delete("/admin/docs/{docID}/metadata", adminMiddleware.RequireAdmin(documentHandlers.HandleDeleteDocumentMetadata))
5158
}
5259
}

templates/admin_dashboard.html.tpl

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,9 @@
5151

5252
<!-- Documents List -->
5353
<div class="bg-white rounded-lg shadow-sm border border-slate-200 p-6">
54-
<div class="flex items-center justify-between mb-6">
55-
<div>
56-
<h1 class="text-2xl font-bold text-slate-900">{{index .T "admin.title"}}</h1>
57-
<p class="text-slate-600 mt-1">{{index .T "admin.subtitle"}}</p>
58-
</div>
59-
<div class="flex items-center space-x-2">
60-
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
61-
<span class="text-sm text-slate-600">{{index .T "admin.connected"}}</span>
62-
</div>
54+
<div class="mb-6">
55+
<h1 class="text-2xl font-bold text-slate-900">{{index .T "admin.title"}}</h1>
56+
<p class="text-slate-600 mt-1">{{index .T "admin.subtitle"}}</p>
6357
</div>
6458

6559
{{if .Documents}}

0 commit comments

Comments
 (0)