chore: wire Carplace into dev and production stacks
Build & Deploy / Build & Push Docker Image (push) Successful in 2m55s
Test / Type Check (all packages) (push) Successful in 54s
Build & Deploy / Deploy to VPS (push) Successful in 4s
Test / API Unit Tests (push) Failing after 48s
Test / Homepage Unit Tests (push) Successful in 43s
Test / Storefront Unit Tests (push) Failing after 40s
Test / Admin Unit Tests (push) Successful in 40s
Test / Dashboard Unit Tests (push) Failing after 42s
Test / API Integration Tests (push) Successful in 1m0s

This commit is contained in:
root
2026-07-02 18:15:42 -04:00
parent ad5f5ebab7
commit 7ff2dbb139
214 changed files with 5258 additions and 5906 deletions
+4 -4
View File
@@ -559,16 +559,16 @@ router.post('/subscriptions/:subscriptionId/cancel', requireAdminAuth, requireAd
// ─── Site config ───────────────────────────────────────────────
router.get('/site-config/storefront-homepage', requireAdminAuth, async (req, res, next) => {
router.get('/site-config/carplace-homepage', requireAdminAuth, async (req, res, next) => {
try {
ok(res, await service.getStorefrontHomepage())
ok(res, await service.getCarplaceHomepage())
} catch (err) { next(err) }
})
router.patch('/site-config/storefront-homepage', requireAdminAuth, requireAdminRole('SUPPORT'), async (req, res, next) => {
router.patch('/site-config/carplace-homepage', requireAdminAuth, requireAdminRole('SUPPORT'), async (req, res, next) => {
try {
const { homepage } = parseBody(homepageUpdateSchema, req)
ok(res, await service.updateStorefrontHomepage(homepage, req.admin.id, req.ip))
ok(res, await service.updateCarplaceHomepage(homepage, req.admin.id, req.ip))
} catch (err) { next(err) }
})
+28 -20
View File
@@ -1,5 +1,13 @@
import { z } from 'zod'
import type { StorefrontHomepageContent, StorefrontHomepageHowItWorksStep, StorefrontHomepageMetric, StorefrontHomepagePillar, StorefrontHomepageSectionType, StorefrontHomepageStep, StorefrontHomepageTestimonial } from '@rentaldrivego/types'
import type {
CarplaceHomepageContent,
CarplaceHomepageHowItWorksStep,
CarplaceHomepageMetric,
CarplaceHomepagePillar,
CarplaceHomepageSectionType,
CarplaceHomepageStep,
CarplaceHomepageTestimonial,
} from '@rentaldrivego/types'
export const loginSchema = z.object({
email: z.string().email().max(255).trim().toLowerCase(),
@@ -154,7 +162,7 @@ export const adminCompanyUpdateSchema = z.object({
publicCity: nullableString, publicCountry: nullableString, websiteUrl: nullableUrl,
whatsappNumber: nullableString, defaultLocale: z.string().min(2).optional(),
defaultCurrency: z.literal('MAD').optional(),
isListedOnMarketplace: z.boolean().optional(),
isListedOnCarplace: z.boolean().optional(),
homePageConfig: z.any().optional(),
menuConfig: z.any().optional(),
}).optional(),
@@ -175,35 +183,35 @@ export const adminCompanyUpdateSchema = z.object({
}).optional(),
})
const storefrontMetricSchema: z.ZodType<StorefrontHomepageMetric> = z.object({ value: z.string().min(1), label: z.string().min(1) })
const storefrontPillarSchema: z.ZodType<StorefrontHomepagePillar> = z.object({ title: z.string().min(1), body: z.string().min(1) })
const storefrontStepSchema: z.ZodType<StorefrontHomepageStep> = z.object({ step: z.string().min(1), title: z.string().min(1), body: z.string().min(1) })
const storefrontHowItWorksStepSchema: z.ZodType<StorefrontHomepageHowItWorksStep> = z.object({ number: z.string().min(1), title: z.string().min(1), description: z.string().min(1) })
const storefrontTestimonialSchema: z.ZodType<StorefrontHomepageTestimonial> = z.object({ quote: z.string().min(1), author: z.string().min(1), role: z.string().min(1), company: z.string().optional() })
const storefrontSectionSchema: z.ZodType<StorefrontHomepageSectionType> = z.enum(['hero', 'surface', 'pillars', 'audiences', 'features', 'howitworks', 'steps', 'testimonials', 'closing'])
const carplaceMetricSchema: z.ZodType<CarplaceHomepageMetric> = z.object({ value: z.string().min(1), label: z.string().min(1) })
const carplacePillarSchema: z.ZodType<CarplaceHomepagePillar> = z.object({ title: z.string().min(1), body: z.string().min(1) })
const carplaceStepSchema: z.ZodType<CarplaceHomepageStep> = z.object({ step: z.string().min(1), title: z.string().min(1), body: z.string().min(1) })
const carplaceHowItWorksStepSchema: z.ZodType<CarplaceHomepageHowItWorksStep> = z.object({ number: z.string().min(1), title: z.string().min(1), description: z.string().min(1) })
const carplaceTestimonialSchema: z.ZodType<CarplaceHomepageTestimonial> = z.object({ quote: z.string().min(1), author: z.string().min(1), role: z.string().min(1), company: z.string().optional() })
const carplaceSectionSchema: z.ZodType<CarplaceHomepageSectionType> = z.enum(['hero', 'surface', 'pillars', 'audiences', 'features', 'howitworks', 'steps', 'testimonials', 'closing'])
const storefrontHomepageContentSchema: z.ZodType<StorefrontHomepageContent> = z.object({
sections: z.array(storefrontSectionSchema).min(1),
const carplaceHomepageContentSchema: z.ZodType<CarplaceHomepageContent> = z.object({
sections: z.array(carplaceSectionSchema).min(1),
heroKicker: z.string().min(1), heroTitle: z.string().min(1), heroBody: z.string().min(1),
startTrial: z.string().min(1), exploreVehicles: z.string().min(1),
surfaceLabel: z.string().min(1), surfaceTitle: z.string().min(1), surfaceBody: z.string().min(1),
liveLabel: z.string().min(1), trustedFleets: z.string().min(1), brandedFlows: z.string().min(1), multiTenant: z.string().min(1),
companyKicker: z.string().min(1), companyTitle: z.string().min(1), companyBody: z.string().min(1),
renterKicker: z.string().min(1), renterTitle: z.string().min(1), renterBody: z.string().min(1),
pillars: z.array(storefrontPillarSchema).length(3),
metrics: z.array(storefrontMetricSchema).length(3),
pillars: z.array(carplacePillarSchema).length(3),
metrics: z.array(carplaceMetricSchema).length(3),
featureLabel: z.string().min(1), features: z.array(z.string().min(1)).min(1),
howitworksKicker: z.string().min(1), howitworksTitle: z.string().min(1), howitworksSteps: z.array(storefrontHowItWorksStepSchema).min(1),
stepsTitle: z.string().min(1), steps: z.array(storefrontStepSchema).length(3), stepLabel: z.string().min(1),
testimonialsKicker: z.string().min(1), testimonialsTitle: z.string().min(1), testimonials: z.array(storefrontTestimonialSchema).min(1),
howitworksKicker: z.string().min(1), howitworksTitle: z.string().min(1), howitworksSteps: z.array(carplaceHowItWorksStepSchema).min(1),
stepsTitle: z.string().min(1), steps: z.array(carplaceStepSchema).length(3), stepLabel: z.string().min(1),
testimonialsKicker: z.string().min(1), testimonialsTitle: z.string().min(1), testimonials: z.array(carplaceTestimonialSchema).min(1),
readyKicker: z.string().min(1), readyTitle: z.string().min(1), readyBody: z.string().min(1),
viewPricing: z.string().min(1), createWorkspace: z.string().min(1),
})
export const storefrontHomepageConfigSchema = z.object({
en: storefrontHomepageContentSchema,
fr: storefrontHomepageContentSchema,
ar: storefrontHomepageContentSchema,
export const carplaceHomepageConfigSchema = z.object({
en: carplaceHomepageContentSchema,
fr: carplaceHomepageContentSchema,
ar: carplaceHomepageContentSchema,
})
export const idParamSchema = z.object({
@@ -300,7 +308,7 @@ export const billingRefundSchema = z.object({
})
export const homepageUpdateSchema = z.object({
homepage: storefrontHomepageConfigSchema,
homepage: carplaceHomepageConfigSchema,
})
export const pricingUpdateSchema = z.object({
+6 -6
View File
@@ -3,7 +3,7 @@ import crypto from 'crypto'
import { authenticator } from 'otplib'
import { signActorToken } from '../../security/tokens'
import qrcode from 'qrcode'
import { getStorefrontHomepageContent, saveStorefrontHomepageContent } from '../../services/platformContentService'
import { getCarplaceHomepageContent, saveCarplaceHomepageContent } from '../../services/platformContentService'
import { sendTransactionalEmail } from '../../services/notificationService'
import * as presenter from './admin.presenter'
import * as repo from './admin.repo'
@@ -402,16 +402,16 @@ export function issueBillingRefund(
return billingService.issueRefund(invoiceId, data, adminId, ip)
}
export function getStorefrontHomepage() {
return getStorefrontHomepageContent()
export function getCarplaceHomepage() {
return getCarplaceHomepageContent()
}
export async function updateStorefrontHomepage(homepage: any, adminId: string, ip?: string) {
const saved = await saveStorefrontHomepageContent(homepage)
export async function updateCarplaceHomepage(homepage: any, adminId: string, ip?: string) {
const saved = await saveCarplaceHomepageContent(homepage)
await repo.createAuditLog({
adminUserId: adminId,
action: 'UPDATE',
resource: 'StorefrontHomepage',
resource: 'CarplaceHomepage',
after: toAuditJson(saved),
ipAddress: ip,
userAgent: undefined,