feat(catalog): OEM oyları detay sayfasına taşındı + topluluk muadil önerileri

Parça tablosu sadeleşti: Uyum ve Adet kolonları kalktı, liste açılışındaki
toplu istekler (/oem-votes/lookup ve /p/matched) tamamen kaldırıldı. Her OEM
kodu artık koşulsuz /dashboard/oem/$code'a linklenir — P eşleşmesi olmayan
kodda da sayfa dolu: topluluk oyu kartı, muadil önerileri ve ters katalog.

OEM detay sayfası: OemVoteCard (uyumlu/uyumsuz, sayaçlar, puan toast'ı,
puanlama özeti + Parça Uzmanları linki) ve OemSuggestionsSection — eşleşme
bulunamayan kodlar için kullanıcıdan marka + parça kodu önerisi toplar.
Öneriler oem_suggestions tablosunda (kullanıcı+kod+normalize öneri başına
tek satır, ON CONFLICT yutulur), markaya+normalize koda göre gruplanıp
"× N kullanıcı" rozetiyle listelenir; önerilen kod kendi detayına linklenir.
Şimdilik öneri puan kazandırmaz; status kolonu moderasyon kancası.

API: oem-suggestions modülü (POST 10/dk throttle, GET ?code=), migration
0016_oem_suggestions. Eski tablo-içi OemVoteButtons bileşeni silindi.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 00:26:20 +03:00
parent d922fb7a02
commit 4072c6e736
18 changed files with 795 additions and 255 deletions

View File

@@ -711,3 +711,34 @@ export const oemVotePoints = pgTable(
index("oem_vote_points_user_id_idx").on(table.userId),
],
);
// ─── OEM Suggestions (community cross-references) ─────
// Crowd-sourced equivalents for an OEM code: "this code's compatible part is
// <brand> <suggestedCode>", collected on the OEM detail page — primarily for
// codes the P snapshot can't match. suggestedCodeNorm (upper, alphanumerics
// only) powers grouping and the per-user dedup constraint; display keeps the
// submitted casing. `status` is a moderation hook (active|hidden) — no rows
// are hidden automatically today.
export const oemSuggestions = pgTable(
"oem_suggestions",
{
id: uuid("id").primaryKey().defaultRandom(),
userId: uuid("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
oemCode: varchar("oem_code", { length: 100 }).notNull(),
brand: varchar("brand", { length: 80 }).notNull(),
suggestedCode: varchar("suggested_code", { length: 100 }).notNull(),
suggestedCodeNorm: varchar("suggested_code_norm", { length: 100 }).notNull(),
status: varchar("status", { length: 16 }).default("active").notNull(),
createdAt: timestamp("created_at", { withTimezone: true }).defaultNow().notNull(),
},
(table) => [
uniqueIndex("oem_suggestions_user_oem_code_idx").on(
table.userId,
table.oemCode,
table.suggestedCodeNorm,
),
index("oem_suggestions_oem_code_idx").on(table.oemCode),
],
);