Some checks failed
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 1m23s
Deploy / Build API Image (push) Failing after 33s
Deploy / Deploy to Staging (push) Has been skipped
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 9s
Deploy / Build Web Image (push) Failing after 11s
Deploy / Build AI Services Image (push) Failing after 9s
E2E Tests / Playwright E2E (push) Failing after 18s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 2s
Security Scanning / Trivy Scan — API Image (push) Failing after 59s
Security Scanning / Trivy Scan — Web Image (push) Failing after 51s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 34s
Security Scanning / Trivy Filesystem Scan (push) Failing after 24s
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Security Scanning / Security Gate (push) Failing after 1s
Deploy / Rollback Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Deploy / Rollback Staging (push) Has been skipped
Foundation for Phase E (AI advisor / AI valuation on listing detail).
An admin sets the Anthropic Claude credentials once in the new
"/admin/settings/ai" page; downstream features read them via
SystemSettingsService.
Database
--------
- New Prisma model SystemSetting { key @id, value Text, valueType,
isSecret, updatedAt, updatedBy }. db:push applied cleanly.
Backend
-------
- SystemSettingsService — canonical getter/setter for
ai.api_url / ai.api_key / ai.model. maskApiKey() returns the last 4
chars prefixed with "sk-ant-...". Exposes unmasked getAiSettings()
for server-side consumers (AI advisor handlers).
- GET /admin/settings/ai — returns { apiUrl, apiKeyMasked, model,
hasApiKey, updatedAt }. Never emits the raw key.
- PATCH /admin/settings/ai — body accepts partial { apiUrl, apiKey,
model }. apiKey sentinel "__UNCHANGED__" preserves the stored value;
empty string clears it; any other value overwrites.
- CQRS: get-ai-settings query + update-ai-settings command. Registered
in admin.module.ts; service exported via modules/admin/index.ts so
Phase E can inject it.
Frontend
--------
- adminApi.getAiSettings() / updateAiSettings() added to
lib/admin-api.ts with shared AiSettings + UpdateAiSettingsPayload
types.
- New Lucide-only nav entry "Cài đặt AI" (Sparkles) in admin layout.
- /admin/settings/ai/page.tsx — Card with API URL input, masked API
key input with Eye/EyeOff toggle, "Xoá key" button, model Select
(claude-opus-4-5 / sonnet-4-5 / haiku-4-5 + custom input), save
button with inline success/error banners, "last updated" timestamp.
- i18n keys adminNav.settings + adminNav.aiSettings in vi.json/en.json.
Constraints
-----------
- No new packages. Runtime imports for NestJS-DI classes preserved.
- Key NOT encrypted at rest (MVP); documented in service comment as
future hardening.
- Page inherits existing admin auth guard via (admin) layout.
Verification
------------
- API typecheck clean.
- Web typecheck clean in touched files.
- API suite: 1975 / 1975 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
303 lines
10 KiB
JSON
303 lines
10 KiB
JSON
{
|
|
"metadata": {
|
|
"title": "GoodGo — Vietnam Real Estate Platform",
|
|
"description": "GoodGo — smart real estate platform in Vietnam. Buy, sell, and rent properties easily with over 10,000+ listings nationwide.",
|
|
"ogTitle": "GoodGo — Vietnam Real Estate Platform",
|
|
"ogDescription": "Buy, sell, and rent properties easily with GoodGo — Vietnam's leading smart real estate platform."
|
|
},
|
|
"common": {
|
|
"goodgo": "GoodGo",
|
|
"loading": "Loading...",
|
|
"retry": "Retry",
|
|
"retrying": "Retrying...",
|
|
"goHome": "Go to homepage",
|
|
"search": "Search",
|
|
"login": "Login",
|
|
"register": "Register",
|
|
"logout": "Logout",
|
|
"admin": "Admin",
|
|
"dashboard": "Dashboard",
|
|
"profile": "Profile",
|
|
"errorCode": "Error code: {code}",
|
|
"retriedCount": "Retried {count} times",
|
|
"allRightsReserved": "© 2026 GoodGo. All rights reserved.",
|
|
"skipToContent": "Skip to main content"
|
|
},
|
|
"nav": {
|
|
"home": "Home",
|
|
"search": "Search",
|
|
"pricing": "Pricing",
|
|
"projects": "Projects",
|
|
"industrialParks": "Industrial Parks",
|
|
"transfer": "Transfer",
|
|
"mainNav": "Main navigation",
|
|
"dashboardNav": "Dashboard",
|
|
"adminNav": "Administration",
|
|
"openMenu": "Open menu",
|
|
"closeMenu": "Close menu"
|
|
},
|
|
"dashboard": {
|
|
"title": "Dashboard",
|
|
"listings": "Listings",
|
|
"createListing": "Create listing",
|
|
"catalogs": "Catalogs",
|
|
"manageProjects": "Manage projects",
|
|
"manageIndustrialParks": "Manage industrial parks",
|
|
"inquiries": "Inquiries",
|
|
"leads": "Leads",
|
|
"analytics": "Analytics",
|
|
"reports": "AI Reports",
|
|
"savedSearches": "Saved searches",
|
|
"aiValuation": "AI Valuation",
|
|
"profile": "Profile",
|
|
"subscription": "Subscription",
|
|
"payments": "Payments",
|
|
"darkMode": "Switch to dark mode",
|
|
"lightMode": "Switch to light mode"
|
|
},
|
|
"adminNav": {
|
|
"dashboard": "Dashboard",
|
|
"users": "User management",
|
|
"moderation": "Content moderation",
|
|
"kyc": "KYC verification",
|
|
"settings": "System settings",
|
|
"aiSettings": "AI settings",
|
|
"closeMenu": "Close menu",
|
|
"openMenu": "Open menu"
|
|
},
|
|
"landing": {
|
|
"heroTitle": "Find your perfect",
|
|
"heroTitleHighlight": "property",
|
|
"heroSubtitle": "Smart real estate platform in Vietnam — buy, sell, and rent properties with ease",
|
|
"searchPlaceholder": "Enter area, project, or keyword...",
|
|
"transactionTypeLabel": "Type",
|
|
"featuresTitle": "GoodGo solutions",
|
|
"featuresSubtitle": "Five core services for Vietnam's real estate market",
|
|
"features": {
|
|
"explore": "Explore",
|
|
"projects": {
|
|
"title": "Projects",
|
|
"description": "Discover the latest apartment, villa, and residential projects"
|
|
},
|
|
"industrial": {
|
|
"title": "Industrial parks",
|
|
"description": "Find industrial land, factories, and warehouses for rent or sale"
|
|
},
|
|
"transfer": {
|
|
"title": "Transfers",
|
|
"description": "Transfer business premises, offices, and retail spaces"
|
|
},
|
|
"valuation": {
|
|
"title": "Property valuation",
|
|
"description": "AI-powered valuation based on real market data"
|
|
},
|
|
"listings": {
|
|
"title": "Listings",
|
|
"description": "Browse for-sale and rental property listings nationwide"
|
|
}
|
|
},
|
|
"featuredTitle": "Featured listings",
|
|
"featuredSubtitle": "Explore the most popular properties",
|
|
"viewAll": "View all",
|
|
"loadError": "Unable to load listings. Please try again.",
|
|
"noFeatured": "No featured listings yet",
|
|
"statsTitle": "GoodGo in numbers",
|
|
"statsSubtitle": "Vietnam's trusted real estate platform",
|
|
"ctaTitle": "Have a property to list?",
|
|
"ctaSubtitle": "List for free today and reach thousands of potential buyers",
|
|
"registerFree": "Register for free",
|
|
"searchNow": "Search now"
|
|
},
|
|
"stats": {
|
|
"listings": "Listings",
|
|
"users": "Users",
|
|
"transactions": "Successful transactions",
|
|
"provinces": "Provinces"
|
|
},
|
|
"footer": {
|
|
"description": "Smart real estate platform in Vietnam",
|
|
"propertyTypes": "Property types",
|
|
"areas": "Areas",
|
|
"support": "Support"
|
|
},
|
|
"propertyTypes": {
|
|
"APARTMENT": "Apartment",
|
|
"HOUSE": "House",
|
|
"VILLA": "Villa",
|
|
"LAND": "Land",
|
|
"OFFICE": "Office",
|
|
"SHOPHOUSE": "Shophouse"
|
|
},
|
|
"transactionTypes": {
|
|
"SALE": "Sale",
|
|
"RENT": "Rent"
|
|
},
|
|
"notFound": {
|
|
"title": "Page not found",
|
|
"description": "The page you are looking for does not exist or has been moved."
|
|
},
|
|
"error": {
|
|
"title": "An error occurred",
|
|
"description": "Sorry, something went wrong. Please try again.",
|
|
"autoRetrying": "Automatically retrying..."
|
|
},
|
|
"language": {
|
|
"label": "Language",
|
|
"vi": "Tiếng Việt",
|
|
"en": "English"
|
|
},
|
|
"auth": {
|
|
"loginTitle": "Login",
|
|
"loginDescription": "Enter your phone number and password to log in",
|
|
"demoAccountsTitle": "Demo accounts (MVP)",
|
|
"demoAccountsHint": "Click to auto-fill. Shared password:",
|
|
"phone": "Phone number",
|
|
"phonePlaceholder": "0912345678",
|
|
"password": "Password",
|
|
"passwordPlaceholder": "Enter password",
|
|
"showPassword": "Show",
|
|
"hidePassword": "Hide",
|
|
"loginButton": "Login",
|
|
"orLoginWith": "Or login with",
|
|
"noAccount": "Don't have an account?",
|
|
"registerLink": "Register",
|
|
"dismiss": "Dismiss",
|
|
"registerTitle": "Register",
|
|
"registerDescription": "Create a new account to start using GoodGo",
|
|
"fullName": "Full name",
|
|
"fullNamePlaceholder": "John Doe",
|
|
"email": "Email",
|
|
"emailPlaceholder": "email@example.com",
|
|
"confirmPassword": "Confirm password",
|
|
"confirmPasswordPlaceholder": "Re-enter password",
|
|
"registerButton": "Register",
|
|
"hasAccount": "Already have an account?",
|
|
"loginLink": "Login",
|
|
"orRegisterWith": "Or register with",
|
|
"oauthErrors": {
|
|
"oauth_failed": "Social login failed. Please try again.",
|
|
"access_denied": "You denied access. Please try again.",
|
|
"invalid_request": "Invalid login request. Please try again.",
|
|
"server_error": "Server error. Please try again later.",
|
|
"temporarily_unavailable": "Service temporarily unavailable. Please try again later.",
|
|
"default": "An error occurred during login. Please try again."
|
|
}
|
|
},
|
|
"pricing": {
|
|
"badge": "Pricing Plans",
|
|
"title": "Choose the right plan for you",
|
|
"subtitle": "From individuals to enterprises — GoodGo has a plan for every real estate need",
|
|
"monthly": "Monthly",
|
|
"yearly": "Yearly",
|
|
"yearlyDiscount": "-17%",
|
|
"perMonth": "month",
|
|
"perYear": "year",
|
|
"loading": "Loading plans...",
|
|
"popular": "Most popular",
|
|
"unlimited": "Unlimited",
|
|
"listingsCount": "listings",
|
|
"savedSearchesCount": "saved searches",
|
|
"photosPerListing": "photos/listing",
|
|
"tiers": {
|
|
"FREE": "Free",
|
|
"AGENT_PRO": "Agent Pro",
|
|
"INVESTOR": "Investor",
|
|
"ENTERPRISE": "Enterprise"
|
|
},
|
|
"tierDescriptions": {
|
|
"FREE": "Get started for free, explore the platform",
|
|
"AGENT_PRO": "For professional real estate agents",
|
|
"INVESTOR": "Analytics tools for investors",
|
|
"ENTERPRISE": "Comprehensive solution for businesses"
|
|
},
|
|
"features": {
|
|
"analytics": "Market analytics",
|
|
"aiValuation": "AI valuation",
|
|
"prioritySupport": "Priority support",
|
|
"featuredListing": "Featured listings",
|
|
"leadManagement": "Lead management",
|
|
"marketReports": "Market reports",
|
|
"portfolioTracking": "Portfolio tracking",
|
|
"apiAccess": "API access"
|
|
},
|
|
"ctaFree": "Register for free",
|
|
"ctaUpgrade": "Get started",
|
|
"ctaEnterprise": "Contact sales",
|
|
"ctaCurrentPlan": "Current plan",
|
|
"ctaDowngrade": "Downgrade",
|
|
"ctaManageSubscription": "Manage subscription",
|
|
"currentPlan": "Current",
|
|
"currentPlanBadge": "You are on the {plan} plan",
|
|
"comparisonTitle": "Compare plans in detail",
|
|
"comparisonSubtitle": "See all features for each plan",
|
|
"feature": "Feature",
|
|
"ctaTitle": "Ready to get started?",
|
|
"ctaDescription": "Sign up today and start your real estate journey with GoodGo",
|
|
"ctaRegister": "Register now",
|
|
"ctaLearnMore": "Learn more"
|
|
},
|
|
"search": {
|
|
"filters": "Filters",
|
|
"allTransactions": "All transactions",
|
|
"allPropertyTypes": "All property types",
|
|
"allAreas": "All areas",
|
|
"allPrices": "All prices",
|
|
"bedrooms": "Bedrooms",
|
|
"bedroomsCount": "{count}+ BR",
|
|
"areaLabel": "Area (m²)",
|
|
"areaFrom": "From",
|
|
"areaTo": "To",
|
|
"district": "District",
|
|
"searchButton": "Search",
|
|
"priceRanges": {
|
|
"under1b": "Under 1 billion",
|
|
"1to3b": "1 - 3 billion",
|
|
"3to5b": "3 - 5 billion",
|
|
"5to10b": "5 - 10 billion",
|
|
"10to20b": "10 - 20 billion",
|
|
"over20b": "Over 20 billion"
|
|
}
|
|
},
|
|
"compare": {
|
|
"title": "Compare properties",
|
|
"subtitle": "Comparing {count} properties",
|
|
"emptyState": "Select at least 2 properties to compare. Go back to search to select.",
|
|
"goToSearch": "Go to search",
|
|
"addMore": "Add more",
|
|
"clearAll": "Clear all",
|
|
"compareNow": "Compare now",
|
|
"needMore": "Need more",
|
|
"selected": "{count}/{max} selected",
|
|
"removeItem": "Remove",
|
|
"addToCompare": "Compare",
|
|
"removeFromCompare": "Remove from compare",
|
|
"added": "Added",
|
|
"loadError": "Unable to load data. Please try again.",
|
|
"retry": "Retry",
|
|
"property": "Property",
|
|
"noImage": "No image",
|
|
"remove": "Remove",
|
|
"price": "Price",
|
|
"transactionType": "Transaction",
|
|
"sale": "Sale",
|
|
"rent": "Rent",
|
|
"propertyType": "Property type",
|
|
"area": "Area",
|
|
"pricePerM2": "Price/m²",
|
|
"bedrooms": "Bedrooms",
|
|
"bathrooms": "Bathrooms",
|
|
"rooms": "rooms",
|
|
"direction": "Direction",
|
|
"floors": "Floors",
|
|
"yearBuilt": "Year built",
|
|
"legalStatus": "Legal status",
|
|
"location": "Location",
|
|
"amenities": "Amenities",
|
|
"projectName": "Project",
|
|
"priceRange": "Price range",
|
|
"areaRange": "Area range",
|
|
"pricePerM2Range": "Price/m² range",
|
|
"average": "Average"
|
|
}
|
|
}
|