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
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:
@@ -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) }
|
||||
})
|
||||
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user