This commit is contained in:
cangui 2025-05-11 11:14:11 +02:00
parent 9841d4e7fa
commit 78392b7817
4 changed files with 97 additions and 35 deletions

View File

@ -285,8 +285,9 @@ func WebhookReceiveHandler(db *gorm.DB) http.HandlerFunc {
entry := body["entry"].([]interface{})[0].(map[string]interface{})
change := entry["changes"].([]interface{})[0].(map[string]interface{})
value := change["value"].(map[string]interface{})
recipientID := value["metadata"].(map[string]interface{})["phone_number_id"].(string)
var user models.User
if err := db.Where("whatsapp_phone_number_id = ?", recipientID).First(&user).Error; err != nil {
log.Println("❌ Utilisateur introuvable pour phone_number_id:", recipientID)
@ -294,48 +295,74 @@ func WebhookReceiveHandler(db *gorm.DB) http.HandlerFunc {
return
}
messages := value["messages"].([]interface{})
if len(messages) > 0 {
msg := messages[0].(map[string]interface{})
from := msg["from"].(string)
msgType := msg["type"].(string)
msgID := msg["id"].(string)
// 📥 Gestion des messages entrants
if messages, ok := value["messages"]; ok {
for _, m := range messages.([]interface{}) {
msg := m.(map[string]interface{})
from := msg["from"].(string)
msgType := msg["type"].(string)
msgID := msg["id"].(string)
var content string
if msgType == "text" {
content = msg["text"].(map[string]interface{})["body"].(string)
}
content := extractMessageContent(msg)
var parentID *uint
if ctx, ok := msg["context"].(map[string]interface{}); ok {
contextID := fmt.Sprintf("%v", ctx["id"])
var parent models.Conversation
if err := db.Where("message_id = ?", contextID).First(&parent).Error; err == nil {
parentID = &parent.ID
content += fmt.Sprintf(" (réponse à %s)", contextID)
var parentID *uint
if ctx, ok := msg["context"].(map[string]interface{}); ok {
contextID := fmt.Sprintf("%v", ctx["id"])
var parent models.Conversation
if err := db.Where("message_id = ?", contextID).First(&parent).Error; err == nil {
parentID = &parent.ID
content += fmt.Sprintf(" (réponse à %s)", contextID)
}
}
conv := models.Conversation{
UserID: user.ID,
From: from,
To: recipientID,
MessageID: msgID,
Type: msgType,
Content: content,
Direction: "Entrant",
Status: "Reçu",
ParentID: parentID,
}
if err := db.Create(&conv).Error; err != nil {
log.Println("❌ Erreur enregistrement conversation:", err)
}
}
}
conv := models.Conversation{
UserID: user.ID,
From: from,
To: recipientID,
MessageID: msgID,
Type: msgType,
Content: content,
Direction: "Entrant",
Status: "Reçu",
ParentID: parentID,
}
// 📦 Gestion des statuts : sent, delivered, read, failed
if statuses, ok := value["statuses"]; ok {
for _, s := range statuses.([]interface{}) {
status := s.(map[string]interface{})
msgID := status["id"].(string)
state := status["status"].(string)
timestamp := status["timestamp"].(string)
if err := db.Create(&conv).Error; err != nil {
log.Println("❌ Erreur enregistrement conversation:", err)
log.Printf("📦 Statut reçu : %s pour %s à %s", state, msgID, timestamp)
update := map[string]interface{}{
"status": state,
}
if state == "read" {
parsed, err := strconv.ParseInt(timestamp, 10, 64)
if err == nil {
update["read_at"] = time.Unix(parsed, 0)
}
}
db.Model(&models.Conversation{}).
Where("message_id = ?", msgID).
Updates(update)
}
}
w.WriteHeader(http.StatusOK)
}
}
func CreateUser(db *gorm.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var user models.User

View File

@ -213,7 +213,8 @@ type Conversation struct {
Type string `gorm:"not null"`
Content string `gorm:"type:text"`
Direction string `gorm:"not null"` // inbound / outbound
Status string `gorm:"type:varchar(20)"` // sent, delivered, read, failed
Status string `gorm:"type:varchar(20)"`
ReadAt *time.Time `gorm:"column:read_at"`
ParentID *uint `gorm:"index"` // lien vers le message parent (nullable)
}

View File

@ -9,7 +9,13 @@
<td>{{ .Type }}</td>
<td>{{ .Content }}</td>
<td>{{ .Status }}</td>
<td>
{{ if .ReadAt.IsZero }}
-
{{ else }}
{{ .ReadAt.Format "2006-01-02 15:04:05" }}
{{ end }}
</td>
</tr>
{{ end }}
{{ end }}

View File

@ -32,8 +32,16 @@
<div class="box mb-4">
<h2 class="subtitle is-6">🔍 Filtres personnalisés</h2>
<div class="columns is-multiline">
<div class="column is-2">
<input type="text" class="input is-small" placeholder="📅 Date" onkeyup="filterTable(0, this.value)">
<div class="column is-4">
<label class="label is-small">📅 Filtrer entre deux dates :</label>
<div class="field has-addons">
<p class="control">
<input type="date" class="input is-small" id="startDate" onchange="filterByDateRange()">
</p>
<p class="control">
<input type="date" class="input is-small" id="endDate" onchange="filterByDateRange()">
</p>
</div>
</div>
<div class="column is-2">
<input type="text" class="input is-small" placeholder="➡️ Direction" onkeyup="filterTable(1, this.value)">
@ -63,8 +71,10 @@
<th onclick="sortTable(3)">📦 Type</th>
<th onclick="sortTable(4)">💬 Contenu</th>
<th onclick="sortTable(5)">✅ Statut</th>
<th onclick="sortTable(6)">📖 Lu le</th>
</tr>
</thead>
<tbody id="conversationRows"
hx-get="/api/user/{{ .UserID }}/conversations"
hx-trigger="load"
@ -81,6 +91,24 @@
</div>
<script>
function filterByDateRange() {
const table = document.getElementById("conversationTable");
const rows = table.tBodies[0].getElementsByTagName("tr");
const start = document.getElementById("startDate").value;
const end = document.getElementById("endDate").value;
for (let i = 0; i < rows.length; i++) {
const dateText = rows[i].children[0].innerText.trim().split(" ")[0]; // yyyy-mm-dd
const rowDate = new Date(dateText);
const showRow =
(!start || rowDate >= new Date(start)) &&
(!end || rowDate <= new Date(end));
rows[i].style.display = showRow ? "" : "none";
}
}
function filterTable(colIndex, filterValue) {
const table = document.getElementById("conversationTable");
const rows = table.getElementsByTagName("tbody")[0].getElementsByTagName("tr");