diff --git a/public/client.js b/public/client.js index 26cd7f2..22409e4 100644 --- a/public/client.js +++ b/public/client.js @@ -523,6 +523,16 @@ async function editMode(dir) { setTimeout(() => { dashboard.style.display = 'none'; }, 500); + + const dueDate = await getDueDate(dir); + const previewElement = document.getElementById('checkdir-duedate'); + previewElement.style.display = "block" + + if (dueDate) { + previewElement.innerHTML = `Fecha de término actual: ${dueDate}`; + } else { + previewElement.innerHTML = "No se pudo cargar la fecha de término."; + } } async function fetchAndStoreData(directoryName) { @@ -617,3 +627,18 @@ function extractSitePath(url) { const match = cleanUrl.match(/^conex\.one\/([^\/?#]+)\/?/); return match ? match[1] : null; } + +async function getDueDate(directory) { + try { + const response = await fetch(`https://api.conex.one/api/duedate/${directory}`); + if (!response.ok) { + throw new Error(`Error: ${response.status} ${response.statusText}`); + } + + const data = await response.json(); + return data.due; + } catch (error) { + console.error("Failed to fetch due date:", error); + return null; + } +} diff --git a/public/index.html b/public/index.html index 30524f8..6db5a53 100644 --- a/public/index.html +++ b/public/index.html @@ -117,8 +117,18 @@
No se puede realizar la compra en este momento
`); + } + }, + async onApprove(data, actions) { + const savedData = JSON.parse(localStorage.getItem('conex_data')) || {}; + try { + const requestData = { + directory: savedData.directory + }; + + const response = await fetch(`https://api.conex.one/api/orders/${data.orderID}/capture`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(requestData), + }); + + const orderData = await response.json(); + // Three cases to handle: + // (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart() + // (2) Other non-recoverable errors -> Show a failure message + // (3) Successful transaction -> Show confirmation or thank you message + + const errorDetail = orderData?.details?.[0]; + + if (errorDetail?.issue === "INSTRUMENT_DECLINED") { + // (1) Recoverable INSTRUMENT_DECLINED -> call actions.restart() + // recoverable state, per https://developer.paypal.com/docs/checkout/standard/customize/handle-funding-failures/ + return actions.restart(); + } else if (errorDetail) { + // (2) Other non-recoverable errors -> Show a failure message + throw new Error(`${errorDetail.description} (${orderData.debug_id})`); + } else if (!orderData.purchase_units) { + throw new Error(JSON.stringify(orderData)); + } else { + // (3) Successful transaction -> Show confirmation or thank you message + // Or go to another URL: actions.redirect('thank_you.html'); + const transaction = + orderData?.purchase_units?.[0]?.payments?.captures?.[0] || + orderData?.purchase_units?.[0]?.payments?.authorizations?.[0]; + checkoutExtendedSuccess(` +
+ Estado: ${transaction.status}
+ ID de transacción: ${transaction.id}
+ Se ha tramitado correctamente la extensión de la fecha de cobro.
+
No se puede realizar la compra en este momento
`); + } + }, +}).render("#paypal-extend-button-container"); + +function checkoutExtendedSuccess(message) { + const container = document.querySelector("#checkout-extended-success-message"); + container.style.display = "block"; + container.innerHTML = message; +} + +function checkoutExtendedError(message) { + const container = document.querySelector("#checkout-extended-error-message"); + container.style.display = "block"; + container.innerHTML = message; +} + diff --git a/public/static/css/style.css b/public/static/css/style.css index d2c09e6..dcd4813 100644 --- a/public/static/css/style.css +++ b/public/static/css/style.css @@ -360,15 +360,13 @@ a { } #dialog button { - margin: 0.5em 0.5em 0 0; + margin: 0em 0.5em 0 0; padding: 0.5em; color: var(--unemph-color); background: var(--background-color); border: 1px solid var(--hover-border); border-radius: 10px; - position: absolute; right: 1.3em; - bottom: 1.5em; } #dialog button:hover { @@ -382,18 +380,14 @@ a { position: absolute; width: 2em; height: 2em; - top: 0em; + top: 0.5em; right: 0.2em; font-size: 0.9em; } #requestChangesButton, #confirmChangesButton { - width: 45%; -} - -#requestChangesButton { - left: 1.78em; + width: 100%; } .input-dialog { @@ -403,7 +397,7 @@ a { background: var(--hover-background); padding: 0.8em; border-radius: var(--border-radius-regular); - margin: 1em auto 1em auto; + margin: 1em auto 0em auto; width: 95%; display: block; } @@ -535,3 +529,10 @@ a { .footer-links a:hover { text-decoration: underline; } + +.buttonsContainer { + display: flex; /* Use Flexbox for layout */ + justify-content: center; /* Center the buttons horizontally */ + align-items: center; /* Center the buttons vertically (if needed) */ + gap: 10px; /* Optional: add space between buttons */ +} diff --git a/server/db.go b/server/db.go index e5786e7..43ac004 100644 --- a/server/db.go +++ b/server/db.go @@ -230,3 +230,21 @@ func FetchSite(db *sql.DB, folder string) (ConexData, error) { return siteData, nil } + +func DueDate(db *sql.DB, folder string) (time.Time, error) { + if len(folder) <= 3 { + return time.Time{}, fmt.Errorf("folder name must be longer than 3 characters") + } + + var due time.Time + if err := db.QueryRow(` + SELECT due FROM sites WHERE folder = $1 + `, folder).Scan(&due); err != nil { + if err == sql.ErrNoRows { + return time.Time{}, fmt.Errorf("no due date found for folder: %s", folder) + } + return time.Time{}, fmt.Errorf("error checking due date: %v", err) + } + + return due, nil +} diff --git a/server/main.go b/server/main.go index 9278f8e..e7654a8 100644 --- a/server/main.go +++ b/server/main.go @@ -132,6 +132,7 @@ func main() { http.HandleFunc("/api/directory/", VerifyDirectoryHandler(db)) http.HandleFunc("/api/fetch/", FetchSiteHandler(db)) http.HandleFunc("/api/upload", UploadFileHandler(s3Client, endpoint, apiEndpoint, apiToken, bucketName, publicEndpoint)) + http.HandleFunc("/api/duedate/", VerifyDueDateHandler(db)) // http.Handle("/", http.FileServer(http.Dir("./public"))) stop := make(chan os.Signal, 1) @@ -462,6 +463,42 @@ func FetchSiteHandler(db *sql.DB) http.HandlerFunc { } } +func VerifyDueDateHandler(db *sql.DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + enableCORS(w) + if r.Method == http.MethodOptions { + w.WriteHeader(http.StatusOK) + return + } + + errClientNotice := "Error verifying duedate against db" + + path := strings.TrimPrefix(r.URL.Path, "/api/duedate/") + parts := strings.Split(path, "/") + folder := parts[0] + if folder == "" { + httpErrorAndLog(w, nil, "Error getting directory", errClientNotice) + return + } + + var response struct { + Due time.Time `json:"due"` + } + + due, err := DueDate(db, folder) + if err != nil { + httpErrorAndLog(w, err, "Error getting due date", errClientNotice) + return + } else { + response.Due = due + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(response) + return + } +} + func enableCORS(w http.ResponseWriter) { w.Header().Set("Access-Control-Allow-Origin", "*") w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")