ui: add B2B olmayan stok (orphans) page

This commit is contained in:
M_Kececi
2026-06-25 14:14:09 +03:00
parent 52b39725ec
commit dfad548963
19 changed files with 594 additions and 71 deletions

View File

@@ -395,6 +395,10 @@ func InitRoutes(pgDB *sql.DB, mssql *sql.DB, ml *mailer.GraphMailer) *mux.Router
"language", "update",
wrapV3(routes.GetTranslationRowsHandler(pgDB)),
)
r.Handle(
"/api/language/translations/runtime",
wrapAuthOnly(http.HandlerFunc(routes.GetRuntimeTranslationsHandler(pgDB))),
).Methods("GET", "OPTIONS")
bindV3(r, pgDB,
"/api/language/translations/{id}", "PUT",
"language", "update",

View File

@@ -695,13 +695,13 @@ LIMIT 1
row.MappingReady = false
switch {
case row.MmitemID <= 0:
row.MappingWarning = "B2B'de urun yok (mmitem)"
row.MappingWarning = "B2B'de urun yok"
case row.Dim1ID <= 0:
row.MappingWarning = "B2B'de renk bu urunde yok (mmitem_dim/dfgrp.code)"
row.MappingWarning = "B2B'de bu urun icin renk yok"
case row.Dim3Code != "" && row.Dim3ID <= 0:
row.MappingWarning = "B2B'de dim3 token eslesmesi yok (mk_dim_token_map: dimval3)"
row.MappingWarning = "B2B'de ikinci renk eslesmesi yok"
case !comboOK:
row.MappingWarning = "B2B'de varyant kombosu yok (mmitem_dim)"
row.MappingWarning = "B2B'de bu urun/renk/ikinci renk kombinasyonu yok"
default:
// Not an orphan; skip.
if baseReady && comboOK {
@@ -861,7 +861,7 @@ func PostProductSeriesMappingsSaveHandler(pg *sql.DB) http.HandlerFunc {
}
mmitemID, err := resolveMmitemIDTx(ctx, tx, code)
if err != nil || mmitemID <= 0 {
http.Error(w, "PG urun bulunamadi: "+code, http.StatusBadRequest)
http.Error(w, "B2B urun bulunamadi: "+code, http.StatusBadRequest)
return
}
// Authoritative dim1 resolver: only allow saving against a color that exists for this product in mmitem_dim.

View File

@@ -134,6 +134,12 @@ type TranslateSelectedPayload struct {
Limit int `json:"limit"`
}
type RuntimeTranslationsResponse struct {
Lang string `json:"lang"`
ByKey map[string]string `json:"by_key"`
ByText map[string]string `json:"by_text"`
}
type BulkUpdateItem struct {
ID int64 `json:"id"`
SourceTextTR *string `json:"source_text_tr"`
@@ -296,6 +302,82 @@ ORDER BY t_key, lang_code
}
}
func GetRuntimeTranslationsHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
lang := normalizeRuntimeTranslationLang(firstNonEmpty(
r.URL.Query().Get("lang"),
r.Header.Get("Accept-Language"),
))
if lang == "" {
lang = "tr"
}
resp := RuntimeTranslationsResponse{
Lang: lang,
ByKey: map[string]string{},
ByText: map[string]string{},
}
rows, err := db.Query(`
WITH base AS (
SELECT DISTINCT ON (t_key)
t_key,
COALESCE(NULLIF(source_text_tr, ''), translated_text, '') AS source_text_tr
FROM mk_translator
WHERE COALESCE(NULLIF(source_text_tr, ''), translated_text, '') <> ''
ORDER BY t_key, CASE WHEN lang_code='tr' THEN 0 ELSE 1 END, updated_at DESC
),
target AS (
SELECT DISTINCT ON (t_key)
t_key,
COALESCE(translated_text, '') AS translated_text
FROM mk_translator
WHERE lang_code=$1
ORDER BY t_key, is_manual DESC, updated_at DESC
)
SELECT
base.t_key,
base.source_text_tr,
CASE
WHEN $1 = 'tr' THEN base.source_text_tr
ELSE COALESCE(NULLIF(target.translated_text, ''), base.source_text_tr)
END AS display_text
FROM base
LEFT JOIN target ON target.t_key = base.t_key
`, lang)
if err != nil {
http.Error(w, "runtime translations query error", http.StatusInternalServerError)
return
}
defer rows.Close()
for rows.Next() {
var key, sourceText, displayText string
if err := rows.Scan(&key, &sourceText, &displayText); err != nil {
http.Error(w, "runtime translations scan error", http.StatusInternalServerError)
return
}
key = strings.TrimSpace(key)
sourceText = normalizeRuntimeTranslationText(sourceText)
displayText = normalizeRuntimeTranslationText(displayText)
if key != "" && displayText != "" {
resp.ByKey[key] = displayText
}
if sourceText != "" && displayText != "" {
resp.ByText[sourceText] = displayText
}
}
if err := rows.Err(); err != nil {
http.Error(w, "runtime translations rows error", http.StatusInternalServerError)
return
}
_ = json.NewEncoder(w).Encode(resp)
}
}
func UpdateTranslationRowHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
@@ -1667,6 +1749,30 @@ func normalizeTranslationLang(v string) string {
return ""
}
func normalizeRuntimeTranslationLang(v string) string {
raw := strings.ToLower(strings.TrimSpace(v))
if raw == "" {
return ""
}
if strings.Contains(raw, ",") {
raw = strings.TrimSpace(strings.Split(raw, ",")[0])
}
if strings.Contains(raw, ";") {
raw = strings.TrimSpace(strings.Split(raw, ";")[0])
}
if strings.Contains(raw, "-") {
raw = strings.TrimSpace(strings.Split(raw, "-")[0])
}
if strings.Contains(raw, "_") {
raw = strings.TrimSpace(strings.Split(raw, "_")[0])
}
return normalizeTranslationLang(raw)
}
func normalizeRuntimeTranslationText(v string) string {
return strings.Join(strings.Fields(strings.TrimSpace(v)), " ")
}
func normalizeTranslationStatus(v string) string {
status := strings.ToLower(strings.TrimSpace(v))
if _, ok := translationStatusSet[status]; ok {