diff --git a/messages/en.json b/messages/en.json index 9f949ad..823a68f 100644 --- a/messages/en.json +++ b/messages/en.json @@ -38,7 +38,9 @@ "login-success": "Login successful!", "register-success": "Registration successful!", "login-required-title": "Login Required", - "login-required-message": "Please sign in or create an account to view match analysis." + "login-required-message": "Please sign in or create an account to view match analysis.", + "login-error": "Login failed!", + "register-error": "Registration failed!" }, "all-right-reserved": "All rights reserved.", "privacy-policy": "Privacy Policy", @@ -155,7 +157,26 @@ "penalty": "Penalty", "half-time": "1st Half", "second-half": "2nd Half", - "assist": "Assist" + "assist": "Assist", + "live-odds": "Live Betting Odds", + "lineup-official-xi": "Official Starting XI", + "lineup-confirmed-squad": "Confirmed Squad", + "lineup-confirmed": "Confirmed", + "lineup-confirmed-live-desc": "The lineup has been officially confirmed.", + "lineup-participation-desc": "Lineup taken from match participation data.", + "lineup-probable-squad": "Probable Squad", + "lineup-probable": "Probable", + "lineup-probable-desc": "Probable lineup based on starting XI data from recent matches. The AI analysis is based on this squad.", + "lineup-info": "Lineup Info", + "lineup-pending": "Awaiting Lineup", + "lineup-pending-desc": "The lineup has not been announced yet. The AI analysis is based on overall team strength and statistics.", + "lineup-tbd": "Lineup not yet known", + "lineup-tbd-sub": "Will be updated close to kick-off", + "lineup-not-announced": "Lineup Not Announced Yet", + "lineup-not-announced-desc": "{home} and {away} lineups will be updated close to kick-off. The AI analysis is based on team statistics and balance of strength.", + "overview": "Overview", + "match-count": "{count} matches", + "odds-legend": "% = model's calibrated hit probability · ↓↑ = opening→closing movement" }, "predictions": { "title": "Predictions", @@ -223,7 +244,27 @@ "no_bet_conditions_met": "The algorithm could not find a safe/valuable bet for this match.", "insufficient_play_score": "Model signal is below the threshold.", "no_ev_edge_minimum_stake": "Passed safety gates but no mathematical edge — minimum stake applied.", - "upset_risk_detected": "High upset risk detected, proceed with caution." + "upset_risk_detected": "High upset risk detected, proceed with caution.", + "confidence_interval_too_wide": "The confidence interval is too wide.", + "confidence_band_low": "The confidence band is low.", + "draw_probability_elevated": "The draw probability looks elevated.", + "balanced_match_risk": "The match looks balanced; there is upset risk.", + "high_total_goal_volatility": "High goal tempo increases the upset risk.", + "mutual_goal_pressure": "Both teams are generating goal threat.", + "late_goal_swing_risk": "High risk of a late goal or score swing.", + "live_match_open_state": "The live match has turned completely open.", + "live_match_active_state": "The live match is more active than expected.", + "live_state_impossible_market": "The live match state invalidates this market.", + "live_score_exceeds_under_line": "The live score is very close to or past the under line.", + "score_model_conflicts_with_under_pick": "The score and xG model conflict with this under pick.", + "score_model_conflicts_with_over_pick": "The score and xG model conflict with this over pick.", + "market_stack_conflict_over25": "The over 2.5 signal conflicts with this market.", + "market_stack_conflict_btts": "The BTTS signal conflicts with this market.", + "live_total_goals_close_to_line": "The live total goals are too close to this line.", + "score_model_conflicts_with_btts_no": "The score and xG model conflict with the BTTS-No pick.", + "score_model_conflicts_with_draw_pick": "The score model does not support the draw pick.", + "score_model_conflicts_with_home_pick": "The score model does not support the home pick.", + "score_model_conflicts_with_away_pick": "The score model does not support the away pick." }, "ev-edge": "Theoretical Edge", "implied-prob": "Market Probability", @@ -360,7 +401,11 @@ "confidence-medium": "Medium", "confidence-low": "Low", "confidence-unknown": "Unknown", - "info": "Info" + "info": "Info", + "unified-confidence": "Confidence Score", + "underdog-ref-tooltip": "Model probability of the underdog side (informational)", + "trap-tooltip": "The market is overconfident", + "no-value-tooltip": "Model agrees with the favourite but the odds are too low" } }, "coupons": { @@ -487,7 +532,8 @@ "freq-mode-active": "Frequency Engine active", "ai-mode-active": "AI Engine active", "engine-mode-label": "Engine Mode", - "engine-mode-help": "AI: Gemini-based AI prediction. Frequency: Database-driven statistical analysis." + "engine-mode-help": "AI: Gemini-based AI prediction. Frequency: Database-driven statistical analysis.", + "all-markets-scanned": "All markets will be scanned" }, "profile": { "title": "Profile", @@ -519,7 +565,14 @@ "leagues": "Leagues", "countries-leagues": "Countries & Leagues", "search-at-least-2": "Type at least 2 characters to search teams.", - "all": "All" + "all": "All", + "back-to-leagues": "Back to Leagues", + "league-not-found": "League Not Found", + "past-matches": "Past Matches", + "not-found": "Not Found", + "no-leagues-found": "No leagues match the selected criteria.", + "team-not-found": "Team Not Found", + "no-teams-found": "No team found matching \"{query}\"." }, "h2h": { "title": "Head to Head", @@ -809,5 +862,71 @@ "remaining": "remaining" } }, - "refund-policy": "Refund Policy" + "refund-policy": "Refund Policy", + "teams": { + "search-placeholder": "Type a team name... (min 2 characters)", + "search-hint": "Type the name of the team you are looking for", + "search-example": "E.g.: Galatasaray, Barcelona, Manchester City", + "no-results": "No results found", + "upcoming": "Upcoming", + "not-found": "Team not found", + "matches-count": "Matches", + "upcoming-matches": "Upcoming Matches", + "past-matches": "Past Matches", + "page-info": "Page {page}/{totalPages} • {total} matches in total", + "no-past-matches-season": "No past matches found for this season" + }, + "v28": { + "avg-cards": "Avg: {avg} cards", + "band": "Band", + "cards-analysis": "Cards Analysis", + "combined-over-rate": "Combined OVER Rate", + "combined-weighting": "Weighted 60% referee + 40% teams", + "confirmations": "Confirm", + "engines-agree": "Engines Agree", + "engines-disagree": "Engines Disagree", + "ft": "FT", + "ht": "HT", + "htft-info": "Historical hit rates of half-time/full-time combinations within a similar odds band.", + "htft-title": "HT/FT Combinations", + "insufficient": "LOW DATA", + "insufficient-data": "Insufficient data — not enough match samples yet.", + "label-away": "FT Away", + "label-btts_yes": "BTTS Yes", + "label-cards_over": "Cards Over", + "label-dc_12": "DC 12", + "label-dc_1x": "DC 1X", + "label-dc_x2": "DC X2", + "label-home": "FT Home", + "label-ht_away": "HT Away", + "label-ht_home": "HT Home", + "label-ht_ou05_over": "HT Over 0.5", + "label-ht_ou15_over": "HT Over 1.5", + "label-htft_11": "HT/FT 1/1", + "label-htft_12": "HT/FT 1/2", + "label-htft_1x": "HT/FT 1/X", + "label-htft_21": "HT/FT 2/1", + "label-htft_22": "HT/FT 2/2", + "label-htft_2x": "HT/FT 2/X", + "label-htft_x1": "HT/FT X/1", + "label-htft_x2": "HT/FT X/2", + "label-htft_xx": "HT/FT X/X", + "label-oe_odd": "Odd", + "label-ou15_over": "Over 1.5", + "label-ou25_over": "Over 2.5", + "label-ou35_over": "Over 3.5", + "matches-count": "{count} matches", + "odds": "Odds", + "over-rate": "Over rate", + "panel-info": "Statistical analysis of results that occurred at similar odds in past matches. Includes Triple Value, Cards Profile, and HT/FT combinations.", + "panel-title": "V28 Odds Band Analysis", + "pass": "PASS", + "referee-profile": "Referee Profile", + "strongest": "Strongest", + "team-profile": "Team Profile", + "triple-value-info": "Comparison of model probability, odds-band statistics, and market odds. A positive edge means the model sees an advantage.", + "triple-value-title": "Value Detection (Triple Value)", + "value": "VALUE", + "value-signals": "{count} Value Signals" + } } \ No newline at end of file diff --git a/messages/tr.json b/messages/tr.json index c9febc3..5d529b8 100644 --- a/messages/tr.json +++ b/messages/tr.json @@ -38,7 +38,9 @@ "login-success": "Giriş başarılı!", "register-success": "Kayıt başarılı!", "login-required-title": "Giriş Yapmanız Gerekiyor", - "login-required-message": "Maç analizlerini görüntülemek için lütfen giriş yapın veya hesap oluşturun." + "login-required-message": "Maç analizlerini görüntülemek için lütfen giriş yapın veya hesap oluşturun.", + "login-error": "Giriş yapılamadı!", + "register-error": "Kayıt yapılamadı!" }, "all-right-reserved": "Tüm hakları saklıdır.", "privacy-policy": "Gizlilik Politikası", @@ -155,7 +157,26 @@ "penalty": "Penaltı", "half-time": "İlk Yarı", "second-half": "İkinci Yarı", - "assist": "Asist" + "assist": "Asist", + "live-odds": "Canlı İddaa Oranları", + "lineup-official-xi": "Resmi İlk 11", + "lineup-confirmed-squad": "Onaylı Kadro", + "lineup-confirmed": "Onaylı", + "lineup-confirmed-live-desc": "Kadro resmi olarak onaylandı.", + "lineup-participation-desc": "Kadro maç katılım verilerinden alındı.", + "lineup-probable-squad": "Muhtemel Kadro", + "lineup-probable": "Muhtemel", + "lineup-probable-desc": "Son maçlardaki ilk 11 verilerine dayalı muhtemel kadro. AI analizi bu kadro üzerinden yapılmaktadır.", + "lineup-info": "Kadro Bilgisi", + "lineup-pending": "Kadro Bekleniyor", + "lineup-pending-desc": "Kadro henüz açıklanmadı. AI analizi, takımların genel güç dengesi ve istatistiklerine dayalı olarak üretilmiştir.", + "lineup-tbd": "Kadro henüz belli değil", + "lineup-tbd-sub": "Maç saatine yakın güncellenecek", + "lineup-not-announced": "Kadro Henüz Açıklanmadı", + "lineup-not-announced-desc": "{home} ve {away} kadroları maç saatine yakın güncellenecektir. AI analizi, takım istatistikleri ve güç dengesi üzerinden yapılmaktadır.", + "overview": "Genel Bakış", + "match-count": "{count} maç", + "odds-legend": "%= modelin tutma olasılığı (kalibre) · ↓↑ = açılış→kapanış hareketi" }, "predictions": { "title": "Tahminler", @@ -223,7 +244,27 @@ "missing_spread_odds": "Handikap oranları eksik.", "no_bet_conditions_met": "Algoritma bu maç için güvenli/değerli bir bahis önerisi bulamadı.", "insufficient_play_score": "Model sinyali eşiğin altında kaldı.", - "no_ev_edge_minimum_stake": "Güvenlik kontrollerini geçti ancak matematik avantaj yok — minimum bahis uygulandı." + "no_ev_edge_minimum_stake": "Güvenlik kontrollerini geçti ancak matematik avantaj yok — minimum bahis uygulandı.", + "confidence_interval_too_wide": "Güven aralığı fazla geniş.", + "confidence_band_low": "Güven bandı düşük.", + "draw_probability_elevated": "Beraberlik olasılığı yükselmiş görünüyor.", + "balanced_match_risk": "Maç dengeli görünüyor, sürpriz riski var.", + "high_total_goal_volatility": "Yüksek gol temposu sürpriz riskini artırıyor.", + "mutual_goal_pressure": "İki takım da gol tehdidi üretiyor.", + "late_goal_swing_risk": "Geç gol veya skor kırılması riski yüksek.", + "live_match_open_state": "Canlı maç tamamen açık oyuna dönmüş durumda.", + "live_match_active_state": "Canlı maç beklenenden daha hareketli ilerliyor.", + "live_state_impossible_market": "Canlı maç durumu bu marketi geçersiz kılıyor.", + "live_score_exceeds_under_line": "Canlı skor, alt seçeneğinin üst sınırına çok yaklaştı veya geçti.", + "score_model_conflicts_with_under_pick": "Skor ve xG modeli bu alt seçeneğiyle çelişiyor.", + "score_model_conflicts_with_over_pick": "Skor ve xG modeli bu üst seçeneğiyle çelişiyor.", + "market_stack_conflict_over25": "2.5 üst sinyali bu marketle çelişiyor.", + "market_stack_conflict_btts": "KG Var sinyali bu marketle çelişiyor.", + "live_total_goals_close_to_line": "Canlı toplam gol sayısı bu çizgiye fazla yaklaştı.", + "score_model_conflicts_with_btts_no": "Skor ve xG modeli KG Yok seçeneğiyle çelişiyor.", + "score_model_conflicts_with_draw_pick": "Skor modeli beraberlik seçeneğini desteklemiyor.", + "score_model_conflicts_with_home_pick": "Skor modeli ev sahibi seçeneğini desteklemiyor.", + "score_model_conflicts_with_away_pick": "Skor modeli deplasman seçeneğini desteklemiyor." }, "ev-edge": "Teorik Avantaj", "implied-prob": "Piyasa Olasılığı", @@ -360,7 +401,11 @@ "confidence-medium": "Orta", "confidence-low": "Düşük", "confidence-unknown": "Belirsiz", - "info": "Bilgi" + "info": "Bilgi", + "unified-confidence": "Güven Skoru", + "underdog-ref-tooltip": "Underdog tarafının model olasılığı (bilgi amaçlı)", + "trap-tooltip": "Piyasa aşırı güveniyor", + "no-value-tooltip": "Model favoriyle hemfikir ama oran çok düşük" } }, "coupons": { @@ -487,7 +532,8 @@ "freq-mode-active": "Frekans Motoru aktif", "ai-mode-active": "AI Motoru aktif", "engine-mode-label": "Motor Seçimi", - "engine-mode-help": "AI: Gemini tabanlı yapay zeka tahmini. Frekans: Veritabanı tabanlı istatistiksel analiz." + "engine-mode-help": "AI: Gemini tabanlı yapay zeka tahmini. Frekans: Veritabanı tabanlı istatistiksel analiz.", + "all-markets-scanned": "Tüm marketler taranacak" }, "profile": { "title": "Profil", @@ -519,7 +565,14 @@ "leagues": "Ligler", "countries-leagues": "Ülkeler & Ligler", "search-at-least-2": "Takım aramak için en az 2 karakter yazın.", - "all": "Tümü" + "all": "Tümü", + "back-to-leagues": "Liglere Dön", + "league-not-found": "Lig Bulunamadı", + "past-matches": "Geçmiş Maçlar", + "not-found": "Bulunamadı", + "no-leagues-found": "Seçili kriterlere uygun lig bulunamadı.", + "team-not-found": "Takım Bulunamadı", + "no-teams-found": "\"{query}\" aramasıyla eşleşen bir takım bulunamadı." }, "h2h": { "title": "Karşılıklı Karşılaşma", @@ -809,5 +862,71 @@ "remaining": "kalan" } }, - "refund-policy": "İade Politikası" + "refund-policy": "İade Politikası", + "teams": { + "search-placeholder": "Takım adı yazın... (min 2 karakter)", + "search-hint": "Aramak istediğiniz takımın adını yazın", + "search-example": "Örnek: Galatasaray, Barcelona, Manchester City", + "no-results": "Sonuç bulunamadı", + "upcoming": "Yaklaşan", + "not-found": "Takım bulunamadı", + "matches-count": "Maç", + "upcoming-matches": "Yaklaşan Maçlar", + "past-matches": "Geçmiş Maçlar", + "page-info": "Sayfa {page}/{totalPages} • Toplam {total} maç", + "no-past-matches-season": "Bu sezonda geçmiş maç bulunamadı" + }, + "v28": { + "avg-cards": "Ort: {avg} kart", + "band": "Band", + "cards-analysis": "Kart Analizi", + "combined-over-rate": "Kombine ÜST Oranı", + "combined-weighting": "%60 Hakem + %40 Takım ağırlıklı", + "confirmations": "Onay", + "engines-agree": "Motorlar Uyumlu", + "engines-disagree": "Motorlar Farklı", + "ft": "MS", + "ht": "İY", + "htft-info": "İlk yarı sonucu ve maç sonucu kombinasyonlarının tarihsel oran bandındaki gerçekleşme oranları.", + "htft-title": "İY/MS Kombinasyonları", + "insufficient": "YETERSİZ", + "insufficient-data": "Yetersiz veri — henüz yeterli maç örneği bulunamadı.", + "label-away": "MS Dep", + "label-btts_yes": "KG Var", + "label-cards_over": "Kart ÜST", + "label-dc_12": "ÇŞ 12", + "label-dc_1x": "ÇŞ 1X", + "label-dc_x2": "ÇŞ X2", + "label-home": "MS Ev", + "label-ht_away": "İY Dep", + "label-ht_home": "İY Ev", + "label-ht_ou05_over": "İY ÜST 0.5", + "label-ht_ou15_over": "İY ÜST 1.5", + "label-htft_11": "İY/MS 1/1", + "label-htft_12": "İY/MS 1/2", + "label-htft_1x": "İY/MS 1/X", + "label-htft_21": "İY/MS 2/1", + "label-htft_22": "İY/MS 2/2", + "label-htft_2x": "İY/MS 2/X", + "label-htft_x1": "İY/MS X/1", + "label-htft_x2": "İY/MS X/2", + "label-htft_xx": "İY/MS X/X", + "label-oe_odd": "Tek", + "label-ou15_over": "ÜST 1.5", + "label-ou25_over": "ÜST 2.5", + "label-ou35_over": "ÜST 3.5", + "matches-count": "{count} maç", + "odds": "Oran", + "over-rate": "Üst oranı", + "panel-info": "Geçmiş maçlarda benzer oranlarda gerçekleşen sonuçların istatistiksel analizi. Triple Value, Kart Profili ve İY/MS kombinasyonlarını içerir.", + "panel-title": "V28 Oran Bandı Analizi", + "pass": "PAS", + "referee-profile": "Hakem Profili", + "strongest": "En güçlü", + "team-profile": "Takım Profili", + "triple-value-info": "Model olasılığı, oran bandı istatistiği ve piyasa oranı karşılaştırması. Edge pozitifse model avantaj görüyor demektir.", + "triple-value-title": "Değer Tespiti (Triple Value)", + "value": "DEĞER", + "value-signals": "{count} Değer Sinyali" + } } \ No newline at end of file diff --git a/src/app/[locale]/(auth)/signin/signin-form.tsx b/src/app/[locale]/(auth)/signin/signin-form.tsx index 0ca3c44..e3ce8da 100644 --- a/src/app/[locale]/(auth)/signin/signin-form.tsx +++ b/src/app/[locale]/(auth)/signin/signin-form.tsx @@ -71,7 +71,7 @@ export default function SignInForm() { router.replace("/home"); } catch (error) { toaster.error({ - title: (error as Error).message || "Giriş yaparken hata oluştu!", + title: (error as Error).message || t("auth.login-error"), type: "error", }); } finally { diff --git a/src/components/auth/login-modal.tsx b/src/components/auth/login-modal.tsx index 7f7021f..466eca8 100644 --- a/src/components/auth/login-modal.tsx +++ b/src/components/auth/login-modal.tsx @@ -101,12 +101,12 @@ export function LoginModal({ onOpenChange(false); toaster.success({ - title: t("auth.login-success") || "Giriş başarılı!", + title: t("auth.login-success"), type: "success", }); } catch (error) { toaster.error({ - title: (error as Error).message || "Giriş yapılamadı!", + title: (error as Error).message || t("auth.login-error"), type: "error", }); } finally { @@ -138,12 +138,12 @@ export function LoginModal({ onOpenChange(false); toaster.success({ - title: t("auth.register-success") || "Kayıt başarılı!", + title: t("auth.register-success"), type: "success", }); } catch (error) { toaster.error({ - title: (error as Error).message || "Kayıt yapılamadı!", + title: (error as Error).message || t("auth.register-error"), type: "error", }); } finally { diff --git a/src/components/coupons/coupon-builder-content.tsx b/src/components/coupons/coupon-builder-content.tsx index c121fa4..6ef8c4e 100644 --- a/src/components/coupons/coupon-builder-content.tsx +++ b/src/components/coupons/coupon-builder-content.tsx @@ -569,7 +569,7 @@ export default function CouponBuilderContent() { {selectedMarkets.length === 0 && ( - Tüm marketler taranacak + {t("all-markets-scanned")} )} diff --git a/src/components/dashboard/dashboard-content.tsx b/src/components/dashboard/dashboard-content.tsx index 185a262..11657d7 100644 --- a/src/components/dashboard/dashboard-content.tsx +++ b/src/components/dashboard/dashboard-content.tsx @@ -265,7 +265,7 @@ export default function DashboardContent() { {/* Left Column — Today's Matches */} diff --git a/src/components/leagues/league-detail-content.tsx b/src/components/leagues/league-detail-content.tsx index 2489d39..6adb551 100644 --- a/src/components/leagues/league-detail-content.tsx +++ b/src/components/leagues/league-detail-content.tsx @@ -85,7 +85,7 @@ export default function LeagueDetailContent({ mb={2} fontWeight="medium" > - Liglere Dön + {t("back-to-leagues")} {leagueQuery.isLoading ? ( @@ -134,7 +134,7 @@ export default function LeagueDetailContent({ ) : ( - Lig Bulunamadı + {t("league-not-found")} )} @@ -163,7 +163,7 @@ export default function LeagueDetailContent({ borderColor={useColorModeValue("gray.200", "gray.800")} > - Geçmiş Maçlar + {t("past-matches")} - Bulunamadı + {t("not-found")} - Seçili kriterlere uygun lig bulunamadı. + {t("no-leagues-found")} ) : ( @@ -709,11 +709,10 @@ export default function LeaguesContent() { textAlign="center" > - Takım Bulunamadı + {t("team-not-found")} - "{debouncedTeamQuery}" aramasıyla eşleşen bir takım - bulunamadı. + {t("no-teams-found", { query: debouncedTeamQuery })} ) : ( diff --git a/src/components/matches/lineups-card.tsx b/src/components/matches/lineups-card.tsx index c0093fb..da9f889 100644 --- a/src/components/matches/lineups-card.tsx +++ b/src/components/matches/lineups-card.tsx @@ -12,6 +12,7 @@ import { Icon, } from "@chakra-ui/react"; import { useColorModeValue } from "@/components/ui/color-mode"; +import { useTranslations } from "next-intl"; import { LuUsers, LuUser, @@ -30,47 +31,49 @@ interface LineupsCardProps { /** * Lineup source metadata used for title, badge, and informational banners. */ -function getLineupSourceMeta(source?: string) { +function getLineupSourceMeta( + source: string | undefined, + t: ReturnType, +) { switch (source) { case "confirmed_live": return { - title: "Resmi İlk 11", - badge: "Onaylı Kadro", + title: t("lineup-official-xi"), + badge: t("lineup-confirmed-squad"), badgeColor: "green" as const, icon: LuShieldCheck, - description: "Kadro resmi olarak onaylandı.", + description: t("lineup-confirmed-live-desc"), }; case "confirmed_participation": return { - title: "Onaylı Kadro", - badge: "Onaylı", + title: t("lineup-confirmed-squad"), + badge: t("lineup-confirmed"), badgeColor: "green" as const, icon: LuShieldCheck, - description: "Kadro maç katılım verilerinden alındı.", + description: t("lineup-participation-desc"), }; case "probable_xi": return { - title: "Muhtemel Kadro", - badge: "Muhtemel", + title: t("lineup-probable-squad"), + badge: t("lineup-probable"), badgeColor: "orange" as const, icon: LuUsers, - description: - "Son maçlardaki ilk 11 verilerine dayalı muhtemel kadro. AI analizi bu kadro üzerinden yapılmaktadır.", + description: t("lineup-probable-desc"), }; case "none": default: return { - title: "Kadro Bilgisi", - badge: "Kadro Bekleniyor", + title: t("lineup-info"), + badge: t("lineup-pending"), badgeColor: "gray" as const, icon: LuClock, - description: - "Kadro henüz açıklanmadı. AI analizi, takımların genel güç dengesi ve istatistiklerine dayalı olarak üretilmiştir.", + description: t("lineup-pending-desc"), }; } } export default function LineupsCard({ match, prediction }: LineupsCardProps) { + const t = useTranslations("matches"); const cardBg = useColorModeValue("white", "gray.800"); const borderColor = useColorModeValue("gray.100", "gray.700"); const headerBg = useColorModeValue("gray.50", "whiteAlpha.50"); @@ -82,7 +85,7 @@ export default function LineupsCard({ match, prediction }: LineupsCardProps) { // Determine lineup source from prediction data quality const source = prediction?.data_quality?.lineup_source; - const meta = getLineupSourceMeta(source); + const meta = getLineupSourceMeta(source, t); // Fallback: If no starting players are marked, but we have players, treat them as probable XI if ( @@ -195,10 +198,10 @@ export default function LineupsCard({ match, prediction }: LineupsCardProps) { gap={1} > - Kadro henüz belli değil + {t("lineup-tbd")} - Maç saatine yakın güncellenecek + {t("lineup-tbd-sub")} )} @@ -259,10 +262,10 @@ export default function LineupsCard({ match, prediction }: LineupsCardProps) { gap={1} > - Kadro henüz belli değil + {t("lineup-tbd")} - Maç saatine yakın güncellenecek + {t("lineup-tbd-sub")} )} @@ -280,7 +283,7 @@ export default function LineupsCard({ match, prediction }: LineupsCardProps) { - Kadro Henüz Açıklanmadı + {t("lineup-not-announced")} - {match.homeTeamName} ve {match.awayTeamName} kadroları maç - saatine yakın güncellenecektir. AI analizi, takım istatistikleri - ve güç dengesi üzerinden yapılmaktadır. + {t("lineup-not-announced-desc", { + home: match.homeTeamName, + away: match.awayTeamName, + })} diff --git a/src/components/matches/match-card.tsx b/src/components/matches/match-card.tsx index e38c92d..daf5e3a 100644 --- a/src/components/matches/match-card.tsx +++ b/src/components/matches/match-card.tsx @@ -9,7 +9,7 @@ import { VStack, Image, } from "@chakra-ui/react"; -import { useTranslations } from "next-intl"; +import { useLocale, useTranslations } from "next-intl"; import { useRouter } from "next/navigation"; import { useSession } from "next-auth/react"; import { motion } from "framer-motion"; @@ -28,6 +28,7 @@ const MotionBox = motion.create(Box); export default function MatchCard({ match }: MatchCardProps) { const t = useTranslations("matches"); const tAuth = useTranslations("auth"); + const locale = useLocale(); const router = useRouter(); const { data: session } = useSession(); const [loginModalOpen, setLoginModalOpen] = useState(false); @@ -107,7 +108,7 @@ export default function MatchCard({ match }: MatchCardProps) { - {matchDate.toLocaleDateString("tr-TR", { + {matchDate.toLocaleDateString(locale, { day: "2-digit", month: "short", hour: "2-digit", diff --git a/src/components/matches/match-detail-content.tsx b/src/components/matches/match-detail-content.tsx index be18d43..1c6ee17 100644 --- a/src/components/matches/match-detail-content.tsx +++ b/src/components/matches/match-detail-content.tsx @@ -14,7 +14,7 @@ import { SimpleGrid, Skeleton, } from "@chakra-ui/react"; -import { useTranslations } from "next-intl"; +import { useLocale, useTranslations } from "next-intl"; import { useParams, useRouter } from "next/navigation"; import { useState } from "react"; import { useColorModeValue } from "@/components/ui/color-mode"; @@ -123,6 +123,7 @@ function MatchDetailSkeleton() { export default function MatchDetailContent() { const t = useTranslations("matches"); const tPred = useTranslations("predictions"); + const locale = useLocale(); const queryClient = useQueryClient(); const { data: meData } = useGetMe(); const usageLimit = meData?.data?.usageLimit; @@ -224,7 +225,7 @@ export default function MatchDetailContent() { const show = (key: string) => activeTab === "all" || activeTab === key; const tabs = [ - { key: "all", label: t("all-matches", { defaultValue: "Hepsi" }) }, + { key: "all", label: t("overview") }, ...(matchEvents.length > 0 ? [{ key: "events", label: t("match-events") }] : []), ...(hasStats ? [{ key: "stats", label: t("statistics") }] : []), { key: "lineups", label: t("lineups") }, @@ -391,7 +392,7 @@ export default function MatchDetailContent() { - {new Date(match.mstUtc).toLocaleDateString("tr-TR", { + {new Date(match.mstUtc).toLocaleDateString(locale, { day: "2-digit", month: "short", year: "numeric", diff --git a/src/components/matches/match-list.tsx b/src/components/matches/match-list.tsx index b8eb414..4abbcf7 100644 --- a/src/components/matches/match-list.tsx +++ b/src/components/matches/match-list.tsx @@ -206,7 +206,7 @@ export default function MatchList({ {league.name} - {league.matches.length} {t("title").toLowerCase()} + {t("match-count", { count: league.matches.length })} diff --git a/src/components/matches/matches-content.tsx b/src/components/matches/matches-content.tsx index 53a0681..8fe2b8c 100644 --- a/src/components/matches/matches-content.tsx +++ b/src/components/matches/matches-content.tsx @@ -1,6 +1,6 @@ "use client"; -import { Box, Flex, Heading, Group, Button } from "@chakra-ui/react"; +import { Box, Flex, Heading, Group, Button, Input } from "@chakra-ui/react"; import { useEffect, useState } from "react"; import { useTranslations } from "next-intl"; import { SlideUp } from "@/components/motion"; @@ -160,8 +160,13 @@ export default function MatchesContent() { - { const dateVal = e.target.value; @@ -173,15 +178,6 @@ export default function MatchesContent() { handleQuickFilterChange("all"); } }} - style={{ - padding: "0.25rem 0.5rem", - borderRadius: "0.375rem", - border: "1px solid var(--chakra-colors-gray-200)", - fontSize: "0.875rem", - background: "transparent", - color: "inherit", - outline: "none", - }} /> @@ -198,7 +194,7 @@ export default function MatchesContent() { {/* Main Content */} {/* League Sidebar (Desktop only) */} diff --git a/src/components/matches/odds-card.tsx b/src/components/matches/odds-card.tsx index 8055bb6..07697da 100644 --- a/src/components/matches/odds-card.tsx +++ b/src/components/matches/odds-card.tsx @@ -3,6 +3,7 @@ import { Box, SimpleGrid, Text, Card, VStack, Flex } from "@chakra-ui/react"; import { useColorModeValue } from "@/components/ui/color-mode"; import type { MatchPredictionDto } from "@/lib/api/predictions/types"; +import { useTranslations } from "next-intl"; // movement: per market (Turkish name) -> per selection -> {open, close} export type OddsMovement = Record< @@ -145,6 +146,7 @@ function MarketBlock({ title, selections, prediction, movement }: MarketBlockPro } export default function OddsCard({ odds, prediction, movement }: OddsCardProps) { + const t = useTranslations("matches"); const cardBg = useColorModeValue("white", "gray.900"); const borderColor = useColorModeValue("gray.100", "gray.800"); @@ -176,10 +178,10 @@ export default function OddsCard({ odds, prediction, movement }: OddsCardProps) - Canlı İddaa Oranları + {t("live-odds")} - %= modelin tutma olasılığı (kalibre) · ↓↑ = açılış→kapanış hareketi + {t("odds-legend")} diff --git a/src/components/matches/prediction-card.tsx b/src/components/matches/prediction-card.tsx index 27d3e74..8e673e0 100644 --- a/src/components/matches/prediction-card.tsx +++ b/src/components/matches/prediction-card.tsx @@ -837,17 +837,17 @@ function SummaryTable({ {getSignalTierLabel(item.signal_tier)} {item.is_underdog_reference ? ( - + Underdog ref. ) : null} {item.betting_brain?.trap_market_flag ? ( - + Trap ) : null} {item.betting_brain?.action === "WATCH_NO_VALUE" ? ( - + No-value ) : null} diff --git a/src/components/matches/v28-odds-band-panel.tsx b/src/components/matches/v28-odds-band-panel.tsx index 322f806..8340eb1 100644 --- a/src/components/matches/v28-odds-band-panel.tsx +++ b/src/components/matches/v28-odds-band-panel.tsx @@ -21,6 +21,7 @@ import { LuTrendingUp, } from "react-icons/lu"; import { useColorModeValue } from "@/components/ui/color-mode"; +import { useTranslations } from "next-intl"; import { Tooltip } from "@/components/ui/overlays/tooltip"; import type { HtftComboKey, @@ -44,32 +45,32 @@ function edgeStr(edge: number): string { return `${sign}${(edge * 100).toFixed(1)}%`; } -const TRIPLE_VALUE_LABELS: Record = { - home: "MS Ev", - away: "MS Dep", - ou25_over: "ÜST 2.5", - btts_yes: "KG Var", - ou15_over: "ÜST 1.5", - ou35_over: "ÜST 3.5", - dc_1x: "ÇŞ 1X", - dc_x2: "ÇŞ X2", - dc_12: "ÇŞ 12", - ht_home: "İY Ev", - ht_away: "İY Dep", - ht_ou05_over: "İY ÜST 0.5", - ht_ou15_over: "İY ÜST 1.5", - oe_odd: "Tek", - cards_over: "Kart ÜST", - htft_11: "İY/MS 1/1", - htft_1x: "İY/MS 1/X", - htft_12: "İY/MS 1/2", - htft_x1: "İY/MS X/1", - htft_xx: "İY/MS X/X", - htft_x2: "İY/MS X/2", - htft_21: "İY/MS 2/1", - htft_2x: "İY/MS 2/X", - htft_22: "İY/MS 2/2", -}; +const TRIPLE_VALUE_KEYS = new Set([ + "home", + "away", + "ou25_over", + "btts_yes", + "ou15_over", + "ou35_over", + "dc_1x", + "dc_x2", + "dc_12", + "ht_home", + "ht_away", + "ht_ou05_over", + "ht_ou15_over", + "oe_odd", + "cards_over", + "htft_11", + "htft_1x", + "htft_12", + "htft_x1", + "htft_xx", + "htft_x2", + "htft_21", + "htft_2x", + "htft_22", +]); const HTFT_DISPLAY: Record = { "11": "1/1", @@ -98,7 +99,7 @@ function TooltipIcon({ content }: { content: string }) { contentProps={{ maxW: "260px", fontSize: "xs", px: 3, py: 2 }} > = 5; @@ -194,11 +196,11 @@ function TripleValueCard({ fontSize="2xs" borderRadius="full" > - DEĞER + {t("value")} ) : hasSample ? ( - PAS + {t("pass")} ) : ( - YETERSİZ + {t("insufficient")} )} @@ -218,20 +220,20 @@ function TripleValueCard({ - Band: {pct(entry.band_rate, 1)} + {t("band")}: {pct(entry.band_rate, 1)} - Oran: {pct(entry.implied_prob, 1)} + {t("odds")}: {pct(entry.implied_prob, 1)} {entry.confirmations !== undefined && ( - Onay: {entry.confirmations}/2 + {t("confirmations")}: {entry.confirmations}/2 )} - {entry.band_sample} maç + {t("matches-count", { count: entry.band_sample })} @@ -267,6 +269,7 @@ function ProgressBar({ } function CardsSection({ cards }: { cards: OddsBandCardsDto }) { + const t = useTranslations("v28"); const cardBg = useColorModeValue("white", "gray.800"); const borderColor = useColorModeValue("gray.200", "gray.700"); const hasData = cards.sample >= 3; @@ -283,11 +286,11 @@ function CardsSection({ cards }: { cards: OddsBandCardsDto }) { - Kart Analizi + {t("cards-analysis")} - Yetersiz veri — henüz yeterli maç örneği bulunamadı. + {t("insufficient-data")} ); @@ -309,11 +312,11 @@ function CardsSection({ cards }: { cards: OddsBandCardsDto }) { - Kart Analizi + {t("cards-analysis")} - {cards.sample} maç + {t("matches-count", { count: cards.sample })} @@ -322,10 +325,10 @@ function CardsSection({ cards }: { cards: OddsBandCardsDto }) { - Hakem Profili + {t("referee-profile")} - Ort: {cards.referee_avg.toFixed(1)} kart + {t("avg-cards", { avg: cards.referee_avg.toFixed(1) })} - Üst oranı: {pct(cards.referee_over_rate, 0)} + {t("over-rate")}: {pct(cards.referee_over_rate, 0)} - {cards.referee_sample} maç + {t("matches-count", { count: cards.referee_sample })} @@ -347,10 +350,10 @@ function CardsSection({ cards }: { cards: OddsBandCardsDto }) { - Takım Profili + {t("team-profile")} - Ort: {cards.team_avg.toFixed(1)} kart + {t("avg-cards", { avg: cards.team_avg.toFixed(1) })} - Üst oranı: {pct(cards.team_over_rate, 0)} + {t("over-rate")}: {pct(cards.team_over_rate, 0)} - {cards.team_sample} maç + {t("matches-count", { count: cards.team_sample })} @@ -377,10 +380,10 @@ function CardsSection({ cards }: { cards: OddsBandCardsDto }) { - Kombine ÜST Oranı + {t("combined-over-rate")} - %60 Hakem + %40 Takım ağırlıklı + {t("combined-weighting")} @@ -402,6 +405,7 @@ function HtftGrid({ }: { htft: Record; }) { + const t = useTranslations("v28"); const cardBg = useColorModeValue("white", "gray.800"); const borderColor = useColorModeValue("gray.200", "gray.700"); @@ -450,10 +454,10 @@ function HtftGrid({ - İY/MS Kombinasyonları + {t("htft-title")} - + {/* Column headers */} @@ -465,7 +469,7 @@ function HtftGrid({ textAlign="center" color="fg.muted" > - MS 1 + {t("ft")} 1 - MS X + {t("ft")} X - MS 2 + {t("ft")} 2 {/* Grid rows */} {HTFT_ROWS.map((row, rowIdx) => { - const rowLabels = ["İY 1", "İY X", "İY 2"]; + const rowLabels = [`${t("ht")} 1`, `${t("ht")} X`, `${t("ht")} 2`]; return ( - {data.sample} maç + {t("matches-count", { count: data.sample })} {isMax && ( - En güçlü:{" "} + {t("strongest")}:{" "} {HTFT_DISPLAY[maxKey]} ({pct(maxRate, 0)}) @@ -589,6 +593,7 @@ interface V28OddsBandPanelProps { } export default function V28OddsBandPanel({ engine }: V28OddsBandPanelProps) { + const t = useTranslations("v28"); const cardBg = useColorModeValue("white", "gray.800"); const borderColor = useColorModeValue("gray.200", "gray.700"); @@ -619,8 +624,8 @@ export default function V28OddsBandPanel({ engine }: V28OddsBandPanelProps) { {/* Engine version badge */} @@ -641,8 +646,8 @@ export default function V28OddsBandPanel({ engine }: V28OddsBandPanelProps) { fontSize="2xs" > {engine.consensus === "AGREE" - ? "Motorlar Uyumlu" - : "Motorlar Farklı"} + ? t("engines-agree") + : t("engines-disagree")} )} {valueHits.length > 0 && ( @@ -652,7 +657,7 @@ export default function V28OddsBandPanel({ engine }: V28OddsBandPanelProps) { borderRadius="full" fontSize="2xs" > - {valueHits.length} Değer Sinyali + {t("value-signals", { count: valueHits.length })} )} @@ -663,15 +668,15 @@ export default function V28OddsBandPanel({ engine }: V28OddsBandPanelProps) { - Değer Tespiti (Triple Value) + {t("triple-value-title")} - + {orderedEntries.map(([key, entry]) => ( ))} diff --git a/src/components/site/home/home-card.tsx b/src/components/site/home/home-card.tsx deleted file mode 100644 index d39d37b..0000000 --- a/src/components/site/home/home-card.tsx +++ /dev/null @@ -1,3303 +0,0 @@ -"use client"; - -import { Button } from "@/components/ui/buttons/button"; -import { CloseButton } from "@/components/ui/buttons/close-button"; -import { - ComboboxControl, - ComboboxEmpty, - ComboboxInput, - ComboboxItem, - ComboboxLabel, - ComboboxRoot, -} from "@/components/ui/collections/combobox"; -import { - ListboxContent, - ListboxItem, - ListboxItemText, - ListboxLabel, - ListboxRoot, -} from "@/components/ui/collections/listbox"; -import { - TreeViewBranchControl, - TreeViewBranchIndentGuide, - TreeViewBranchText, - TreeViewItem, - TreeViewItemText, - TreeViewLabel, - TreeViewNode, - TreeViewRoot, - TreeViewTree, -} from "@/components/ui/collections/treeview"; -import { Alert } from "@/components/ui/feedback/alert"; -import { EmptyState } from "@/components/ui/feedback/empty-state"; -import { - ProgressBar, - ProgressLabel, - ProgressRoot, -} from "@/components/ui/feedback/progress"; -import { Status } from "@/components/ui/feedback/status"; -import { - ProgressCircleRing, - ProgressCircleRoot, - ProgressCircleValueText, -} from "@/components/ui/feedback/progress-circle"; -import { - SkeletonCircle, - SkeletonText, -} from "@/components/ui/feedback/skeleton"; -import { Checkbox } from "@/components/ui/forms/checkbox"; -import { CheckboxCard } from "@/components/ui/forms/checkbox-card"; -import { - ColorPickerArea, - ColorPickerChannelSliders, - ColorPickerContent, - ColorPickerControl, - ColorPickerEyeDropper, - ColorPickerInput, - ColorPickerLabel, - ColorPickerRoot, - ColorPickerSliders, - ColorPickerSwatchGroup, - ColorPickerSwatchTrigger, - ColorPickerTrigger, - ColorPickerValueText, -} from "@/components/ui/forms/color-picker"; -import { Field } from "@/components/ui/forms/field"; -import { - FileUploadClearTrigger, - FileUploadDropzone, - FileUploadFileText, - FileUploadLabel, - FileUploadList, - FileUploadRoot, - FileUploadTrigger, -} from "@/components/ui/forms/file-upload"; -import { InputGroup } from "@/components/ui/forms/input-group"; -import { - NativeSelectField, - NativeSelectRoot, -} from "@/components/ui/forms/native-select"; -import { - NumberInputField, - NumberInputRoot, -} from "@/components/ui/forms/number-input"; -import { - PasswordInput, - PasswordStrengthMeter, -} from "@/components/ui/forms/password-input"; -import { PinInput } from "@/components/ui/forms/pin-input"; -import { Radio, RadioGroup } from "@/components/ui/forms/radio"; -import { - RadioCardItem, - RadioCardLabel, - RadioCardRoot, -} from "@/components/ui/forms/radio-card"; -import { Rating } from "@/components/ui/forms/rating"; -import { SegmentedControl } from "@/components/ui/forms/segmented-control"; -import { Slider } from "@/components/ui/forms/slider"; -import { Switch } from "@/components/ui/forms/switch"; -import { - ActionBarContent, - ActionBarRoot, - ActionBarSelectionTrigger, - ActionBarSeparator, -} from "@/components/ui/overlays/action-bar"; -import { - DialogActionTrigger, - DialogBody, - DialogCloseTrigger, - DialogContent, - DialogFooter, - DialogHeader, - DialogRoot, - DialogTitle, - DialogTrigger, -} from "@/components/ui/overlays/dialog"; -import { - DrawerActionTrigger, - DrawerBody, - DrawerCloseTrigger, - DrawerContent, - DrawerFooter, - DrawerHeader, - DrawerRoot, - DrawerTitle, - DrawerTrigger, -} from "@/components/ui/overlays/drawer"; -import { - HoverCardArrow, - HoverCardContent, - HoverCardRoot, - HoverCardTrigger, -} from "@/components/ui/overlays/hover-card"; -import { - MenuContent, - MenuItem, - MenuItemCommand, - MenuItemText, - MenuRadioItem, - MenuRadioItemGroup, - MenuRoot, - MenuTrigger, - MenuTriggerItem, -} from "@/components/ui/overlays/menu"; -import { - PopoverBody, - PopoverContent, - PopoverFooter, - PopoverHeader, - PopoverRoot, - PopoverTitle, - PopoverTrigger, -} from "@/components/ui/overlays/popover"; -import { InfoTip } from "@/components/ui/overlays/toggle-tip"; -import { Tooltip } from "@/components/ui/overlays/tooltip"; -import { - Blockquote, - BlockquoteIcon, -} from "@/components/ui/typography/blockquote"; -import { - accordionItems, - alertStatuses, - alignments, - boxData, - breadcrumbItems, - colorPalettes, - countries, - fontSizes, - formats, - frameworks, - headings, - images, - items, - itemsTabs, - justifications, - marks, - MAX_FILES, - placements, - radioCardItems, - radioCardItems2, - radioItems, - rootNode, - stats, - steps, - swatches, - variants, -} from "@/data/constants"; -import { - AbsoluteCenter, - Badge, - Box, - ButtonGroup, - Card, - Center, - Checkmark, - Circle, - ClientOnly, - Code, - Collapsible, - ColorPickerFormatSelect, - ColorPickerPositioner, - ComboboxContent, - createListCollection, - createTreeCollection, - DownloadTrigger, - Flex, - Float, - For, - FormatByte, - FormatNumber, - Grid, - GridItem, - Heading, - Highlight, - HStack, - Icon, - IconButton, - Image, - Input, - Kbd, - Mark, - parseColor, - Portal, - ProgressValueText, - ScrollArea, - Separator, - SimpleGrid, - Span, - Spinner, - Square, - Stack, - StackSeparator, - Strong, - Table, - Tabs, - Text, - Textarea, - TreeView, - useFilter, - useHighlight, - useListboxItemContext, - useListCollection, - useTreeViewContext, - VStack, - Wrap, -} from "@chakra-ui/react"; -import React from "react"; -import { Fragment } from "react"; -import { FaBell, FaMoon, FaReact, FaSearch, FaSun } from "react-icons/fa"; -import { FaCartShopping } from "react-icons/fa6"; -import { - HiCheck, - HiColorSwatch, - HiShieldCheck, - HiSortAscending, - HiStar, - HiUpload, - HiX, -} from "react-icons/hi"; -import { IoHeart } from "react-icons/io5"; -import { - LuCheck, - LuChevronRight, - LuFile, - LuFileUp, - LuFolder, - LuImageDown, - LuPackage, - LuPen, - LuPercent, - LuPhone, - LuPhoneForwarded, - LuPlus, - LuShare, - LuShip, - LuSquareCheck, - LuSquareMinus, - LuSquarePlus, - LuTrash, - LuTrash2, - LuUser, - LuVoicemail, - LuX, -} from "react-icons/lu"; -import { Md123, Md3dRotation, MdGraphicEq, MdMail } from "react-icons/md"; -import { toaster } from "@/components/ui/feedback/toaster"; -import { Avatar, AvatarGroup } from "@/components/ui/data-display/avatar"; -import { - ClipboardButton, - ClipboardIconButton, - ClipboardInput, - ClipboardRoot, -} from "@/components/ui/data-display/clipboard"; -import { - DataListItem, - DataListRoot, -} from "@/components/ui/data-display/data-list"; -import { QrCode } from "@/components/ui/data-display/qr-code"; -import { GiSnake } from "react-icons/gi"; -import { - StatDownTrend, - StatHelpText, - StatLabel, - StatRoot, - StatUpTrend, - StatValueText, -} from "@/components/ui/data-display/stat"; -import { - PaginationItems, - PaginationNextTrigger, - PaginationPageText, - PaginationPrevTrigger, - PaginationRoot, -} from "@/components/ui/disclosure/pagination"; -import { Tag } from "@/components/ui/data-display/tag"; -import { - TimelineConnector, - TimelineContent, - TimelineDescription, - TimelineItem, - TimelineRoot, - TimelineTitle, -} from "@/components/ui/data-display/timeline"; -import { - AccordionItem, - AccordionItemContent, - AccordionItemTrigger, - AccordionRoot, -} from "@/components/ui/disclosure/accordion"; -import { - BreadcrumbLink, - BreadcrumbRoot, -} from "@/components/ui/disclosure/breadcrumb"; -import { LiaSlashSolid } from "react-icons/lia"; -import { - StepsCompletedContent, - StepsContent, - StepsItem, - StepsList, - StepsNextTrigger, - StepsPrevTrigger, - StepsRoot, -} from "@/components/ui/disclosure/steps"; -import { PiSmileyNervous, PiSmileyWink } from "react-icons/pi"; - -interface Node { - id: string; - name: string; - children?: Node[]; - childrenCount?: number; -} - -interface TreeNodeProps extends TreeView.NodeProviderProps { - onRemove?: (props: TreeView.NodeProviderProps) => void; - onAdd?: (props: TreeView.NodeProviderProps) => void; -} - -type ErrorLevel = "L" | "M" | "Q" | "H"; - -interface Item { - id: string; - title: string; - content: React.ReactNode; -} - -function HomeCard() { - const contentRef = React.useRef(null); - const [selectedSkills, setSelectedSkills] = React.useState([]); - const [selectedImage, setSelectedImage] = React.useState("mountains"); - const [checked, setChecked] = React.useState(false); - const [radioItemValue, setRadioItemValue] = React.useState("asc"); - const [loading, setLoading] = React.useState(true); - const [errorLevel, setErrorLevel] = React.useState("L"); - - const chunks = useHighlight({ - text: "Endless scale, powered by real humans.", - query: ["Endless", "real humans."], - }); - - const downloadData = async () => { - const res = await fetch("https://picsum.photos/200/300"); - return res.blob(); - }; - - const { contains, startsWith } = useFilter({ sensitivity: "base" }); - - const { collection: frameworkCollection, filter: frameworkFilter } = - useListCollection({ - initialItems: frameworks, - filter: contains, - }); - const { collection: countryCollection, filter: countryFilter } = - useListCollection({ - initialItems: countries, - filter: startsWith, - limit: 10, - }); - - const listboxCollection = createListCollection({ items: frameworks }); - const imagesCollection = createListCollection({ items: images }); - - const currentImage = imagesCollection.items.find( - (img) => img.value === selectedImage, - ); - - const nodeCollection = createTreeCollection({ - nodeToValue: (node) => node.id, - nodeToString: (node) => node.name, - rootNode: rootNode, - }); - - const [tabs, setTabs] = React.useState(itemsTabs); - const [selectedTab, setSelectedTab] = React.useState( - itemsTabs[0].id, - ); - - const uuid = () => { - return Math.random().toString(36).substring(2, 15); - }; - - const addTab = () => { - const newTabs = [...tabs]; - - const uid = uuid(); - - newTabs.push({ - id: uid, - title: `Tab`, - content: `Tab Body`, - }); - - setTabs(newTabs); - setSelectedTab(newTabs[newTabs.length - 1].id); - }; - - const removeTab = (id: string) => { - if (tabs.length >= 1) { - const newTabs = [...tabs].filter((tab) => tab.id !== id); - setTabs(newTabs); - } - }; - - const [collection, setCollection] = React.useState(nodeCollection); - const [selection, setSelection] = React.useState([]); - - const hasSelection = selection.length > 0; - const indeterminate = hasSelection && selection.length < items.length; - - const removeNode = (props: TreeNodeProps) => { - setCollection(collection.remove([props.indexPath])); - }; - - const addNode = (props: TreeNodeProps) => { - const { node, indexPath } = props; - if (!collection.isBranchNode(node)) { - return; - } - - const children = [ - { - id: `untitled-${Date.now()}`, - name: `untitled-${node.children?.length}.tsx`, - }, - ...(node.children || []), - ]; - setCollection(collection.replace(indexPath, { ...node, children })); - }; - - const handleSelectionChange = (details: { value: string[] }) => { - if (details.value.length > 0) { - setSelectedImage(details.value[0]); - } - }; - - const ListboxItemCheckmark = () => { - const itemState = useListboxItemContext(); - return ( - - ); - }; - - const TreeNodeActions = (props: TreeNodeProps) => { - const { onRemove, onAdd, node } = props; - const tree = useTreeViewContext(); - const isBranch = tree.collection.isBranchNode(node); - return ( - - { - e.stopPropagation(); - onRemove?.(props); - }} - > - - - {isBranch && ( - { - e.stopPropagation(); - onAdd?.(props); - tree.expand([node.id]); - }} - > - - - )} - - ); - }; - - return ( - - {/* Font sizes */} - - - - {(fontSize, index) => ( - - Text {fontSize as string} - - )} - - - - {/* Buttons */} - - - - - Primary (teal): - - - - - {(palette, index) => ( - - - {palette as string}: - - - - {(variant, innerIndex) => ( - - )} - - - - )} - - - - Close Button: - - - - - - - IconButton: - - {(variant) => ( - - - - - {variant} - - )} - - - - - - - - - {/* Box */} - - - Box - - - This is a box - - - This is a box with pseudo props - - - This is a disabled box - - - {boxData.imageAlt} - - - - Superhost - - - - - - - {boxData.rating} ({boxData.reviewCount}) - - - - - {boxData.title} - - - {boxData.formattedPrice} • {boxData.beds} beds - - - - - {/* Absolute Center, Center, Square, Circle */} - - - Absolute Center, Center, Square, Circle - - - - - - Absolute Center - - - - - -
- -
-
- - 1 - -
- - - - - - -
-
-
-
- {/* Flex */} - - {/* Flex Align */} - - Flex Align - - - {alignments.map((align) => ( - - -
- {align} -
- -
- ))} - - {/* Flex Justify */} - - Flex Justify - - - - {justifications.map((justify) => ( - - -
- {justify} -
- -
- ))} -
-
- {/* Float */} - - - Float - - - {placements.map((placement, index) => ( - -

{placement}

- - - - {++index} - - - -
- ))} -
-
- {/* Grid */} - - - Grid - - - -
- Sol içerik -
-
- - -
- Sağ içerik -
-
- - -
- Alt içerik -
-
-
-
- {/* Simple Grid */} - - - Simple Grid - - -
- Box1 -
-
- Box2 -
-
- Box3 -
-
- Box4 -
-
-
- {/* Scroll Area */} - - - Scroll Area - - - - - {(variant) => ( - - {`variant="${variant}"`} - - - - Lorem ipsum dolor sit amet consectetur adipisicing elit. - Consequatur ratione quaerat nulla autem. Rerum, deserunt - eius illo sed quas aperiam ad sit tempora ea voluptatum - nemo, expedita facere sunt. Accusantium! Laudantium - maxime numquam et laboriosam dolore tenetur obcaecati - amet aliquid veritatis excepturi sapiente vitae quae - beatae sit exercitationem nam praesentium nobis - expedita, atque explicabo. Consequatur, nostrum numquam. - Iste, perferendis suscipit? Sequi cumque alias nisi - deleniti quam perferendis doloremque ducimus facere, - officiis tempora, illum laborum accusantium id, autem - saepe repellendus. Quos veniam rerum provident officiis - libero minima autem, dolor porro eaque. Alias esse - quibusdam ratione quas quod, modi aperiam praesentium - quam porro consectetur sed unde soluta quo neque quae - delectus accusamus fugiat exercitationem, eligendi nobis - animi nulla nisi. Similique, enim. Consequatur! - - - - - - )} - - - - - - - - {Array.from({ length: 12 }, (_, i) => ( -
- Item {i + 1} -
- ))} -
-
-
- - -
-
-
-
- {/* Separator */} - - - Separator - - xs - - sm - - md - - lg - - - Label (start) - - - - - - Label (end) - - - - - Label (center) - - - - {/* Stack */} - - - Stack - - }> - - - -
HStack
-
- -
- - - -
VStack
-
- -
-
-
- {/* Blockquote */} - - - Blockquote - - -
- Lorem ipsum dolor, sit amet consectetur adipisicing elit. -
-
} - > - Türk milletinin istidadı ve kati kararı medeniyet yolunda durmadan, - yılmadan ilerlemektir.{" "} -
-
-
- {/* Code */} - - - Code - - {`console.log("Hello, world!")`} - {`console.log("Hello, world!")`} - {`console.log("Hello, world!")`} - {`console.log("Hello, world!")`} - - {/* Heading */} - - - Heading - - - {(size, index) => ( - - Heading ({size}) - - )} - - - {/* Highlight & Mark */} - - - Highlight & Mark - - - - - With the Highlight component, you can spotlight, emphasize and - accentuate words. - - - - - {(variant) => ( - - The design system is a - collection of UI elements - - )} - - - - {chunks.map((chunk, index) => { - return chunk.match ? ( - - {chunk.text} - - - ) : ( - {chunk.text} - ); - })} - - - - {/* Kbd */} - - - Kbd - - - ctrl+shift+del - - - - - - - - - Raised - Outline - Subtle - Plain - - - {/* Checkbox & Checkbox Card */} - - - Checkbox & Checkbox Card - - - - {(variant) => ( - - {variant} - - Checkbox - - - )} - - - - - } - addon={ - - Some supporting text - New - - } - /> - - - {/* Color Picker */} - - - Color Picker - - - - Basic Color Picker - - - - - - - - - - - - - - - - Color Picker + Eye Dropper + Swatches - - - - - - - - - - - - - - {swatches.map((item) => ( - - ))} - - - - - - - Swatch Only Color: - - - {swatches.map((item) => ( - - ))} - - - - Channel Sliders - - - - - - - - - {(format, index) => ( - - )} - - - - - - - - {/* Fields, Input, Select, Textarea, Number Input */} - - - Fields, Input, Select, Textarea - - - - - - - - - - - - - } - endElement={} - > - - - - -