feat(default): add real rankings data
This commit is contained in:
+39
-16
@@ -1,4 +1,4 @@
|
||||
export type HeaderNavPricingConfig = {
|
||||
export type HeaderNavAccessConfig = {
|
||||
enabled: boolean
|
||||
requireAuth: boolean
|
||||
}
|
||||
@@ -6,10 +6,11 @@ export type HeaderNavPricingConfig = {
|
||||
export type HeaderNavModulesConfig = {
|
||||
home: boolean
|
||||
console: boolean
|
||||
pricing: HeaderNavPricingConfig
|
||||
pricing: HeaderNavAccessConfig
|
||||
rankings: HeaderNavAccessConfig
|
||||
docs: boolean
|
||||
about: boolean
|
||||
[key: string]: boolean | HeaderNavPricingConfig
|
||||
[key: string]: boolean | HeaderNavAccessConfig
|
||||
}
|
||||
|
||||
export type SidebarSectionConfig = {
|
||||
@@ -26,6 +27,10 @@ export const HEADER_NAV_DEFAULT: HeaderNavModulesConfig = {
|
||||
enabled: true,
|
||||
requireAuth: false,
|
||||
},
|
||||
rankings: {
|
||||
enabled: true,
|
||||
requireAuth: false,
|
||||
},
|
||||
docs: true,
|
||||
about: true,
|
||||
}
|
||||
@@ -74,8 +79,33 @@ const toBoolean = (value: unknown, fallback: boolean): boolean => {
|
||||
const cloneHeaderNavDefault = (): HeaderNavModulesConfig => ({
|
||||
...HEADER_NAV_DEFAULT,
|
||||
pricing: { ...HEADER_NAV_DEFAULT.pricing },
|
||||
rankings: { ...HEADER_NAV_DEFAULT.rankings },
|
||||
})
|
||||
|
||||
const parseAccessModule = (
|
||||
raw: unknown,
|
||||
fallback: HeaderNavAccessConfig
|
||||
): HeaderNavAccessConfig => {
|
||||
if (
|
||||
typeof raw === 'boolean' ||
|
||||
typeof raw === 'string' ||
|
||||
typeof raw === 'number'
|
||||
) {
|
||||
return {
|
||||
enabled: toBoolean(raw, fallback.enabled),
|
||||
requireAuth: fallback.requireAuth,
|
||||
}
|
||||
}
|
||||
if (raw && typeof raw === 'object') {
|
||||
const record = raw as Record<string, unknown>
|
||||
return {
|
||||
enabled: toBoolean(record.enabled, fallback.enabled),
|
||||
requireAuth: toBoolean(record.requireAuth, fallback.requireAuth),
|
||||
}
|
||||
}
|
||||
return { ...fallback }
|
||||
}
|
||||
|
||||
const cloneSidebarDefault = (): SidebarModulesAdminConfig =>
|
||||
Object.entries(SIDEBAR_MODULES_DEFAULT).reduce<SidebarModulesAdminConfig>(
|
||||
(acc, [section, config]) => {
|
||||
@@ -97,23 +127,16 @@ export function parseHeaderNavModules(
|
||||
const result: HeaderNavModulesConfig = {
|
||||
...base,
|
||||
pricing: { ...base.pricing },
|
||||
rankings: { ...base.rankings },
|
||||
}
|
||||
|
||||
Object.entries(parsed).forEach(([key, raw]) => {
|
||||
if (key === 'pricing') {
|
||||
if (raw && typeof raw === 'object') {
|
||||
const rawPricing = raw as Record<string, unknown>
|
||||
result.pricing = {
|
||||
enabled: toBoolean(
|
||||
rawPricing.enabled,
|
||||
base.pricing?.enabled ?? true
|
||||
),
|
||||
requireAuth: toBoolean(
|
||||
rawPricing.requireAuth,
|
||||
base.pricing?.requireAuth ?? false
|
||||
),
|
||||
}
|
||||
}
|
||||
result.pricing = parseAccessModule(raw, base.pricing)
|
||||
return
|
||||
}
|
||||
if (key === 'rankings') {
|
||||
result.rankings = parseAccessModule(raw, base.rankings)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+100
-54
@@ -27,6 +27,8 @@ const headerNavSchema = z.object({
|
||||
console: z.boolean(),
|
||||
pricingEnabled: z.boolean(),
|
||||
pricingRequireAuth: z.boolean(),
|
||||
rankingsEnabled: z.boolean(),
|
||||
rankingsRequireAuth: z.boolean(),
|
||||
docs: z.boolean(),
|
||||
about: z.boolean(),
|
||||
})
|
||||
@@ -53,6 +55,14 @@ const toFormValues = (config: HeaderNavModulesConfig): HeaderNavFormValues => ({
|
||||
config.pricing?.requireAuth === undefined
|
||||
? HEADER_NAV_DEFAULT.pricing.requireAuth
|
||||
: Boolean(config.pricing.requireAuth),
|
||||
rankingsEnabled:
|
||||
config.rankings?.enabled === undefined
|
||||
? HEADER_NAV_DEFAULT.rankings.enabled
|
||||
: Boolean(config.rankings.enabled),
|
||||
rankingsRequireAuth:
|
||||
config.rankings?.requireAuth === undefined
|
||||
? HEADER_NAV_DEFAULT.rankings.requireAuth
|
||||
: Boolean(config.rankings.requireAuth),
|
||||
docs:
|
||||
config.docs === undefined ? HEADER_NAV_DEFAULT.docs : Boolean(config.docs),
|
||||
about:
|
||||
@@ -90,6 +100,11 @@ export function HeaderNavigationSection({
|
||||
enabled: values.pricingEnabled,
|
||||
requireAuth: values.pricingRequireAuth,
|
||||
},
|
||||
rankings: {
|
||||
...(config.rankings ?? HEADER_NAV_DEFAULT.rankings),
|
||||
enabled: values.rankingsEnabled,
|
||||
requireAuth: values.rankingsRequireAuth,
|
||||
},
|
||||
}
|
||||
|
||||
const serialized = serializeHeaderNavModules(payload)
|
||||
@@ -107,7 +122,7 @@ export function HeaderNavigationSection({
|
||||
form.reset(toFormValues(HEADER_NAV_DEFAULT))
|
||||
}
|
||||
|
||||
const modules: Array<{
|
||||
const simpleModules: Array<{
|
||||
key: keyof HeaderNavFormValues
|
||||
title: string
|
||||
description: string
|
||||
@@ -134,6 +149,39 @@ export function HeaderNavigationSection({
|
||||
},
|
||||
]
|
||||
|
||||
const accessModules: Array<{
|
||||
enabledKey: keyof HeaderNavFormValues
|
||||
requireAuthKey: keyof HeaderNavFormValues
|
||||
requireAuthDependsOn: 'pricingEnabled' | 'rankingsEnabled'
|
||||
title: string
|
||||
description: string
|
||||
requireAuthTitle: string
|
||||
requireAuthDescription: string
|
||||
}> = [
|
||||
{
|
||||
enabledKey: 'pricingEnabled',
|
||||
requireAuthKey: 'pricingRequireAuth',
|
||||
requireAuthDependsOn: 'pricingEnabled',
|
||||
title: t('Model Square'),
|
||||
description: t('Public model catalog and pricing page.'),
|
||||
requireAuthTitle: t('Require login to view models'),
|
||||
requireAuthDescription: t(
|
||||
'Visitors must authenticate before accessing the pricing directory.'
|
||||
),
|
||||
},
|
||||
{
|
||||
enabledKey: 'rankingsEnabled',
|
||||
requireAuthKey: 'rankingsRequireAuth',
|
||||
requireAuthDependsOn: 'rankingsEnabled',
|
||||
title: t('Rankings'),
|
||||
description: t('Public rankings page based on live usage data.'),
|
||||
requireAuthTitle: t('Require login to view rankings'),
|
||||
requireAuthDescription: t(
|
||||
'Visitors must authenticate before accessing the rankings page.'
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<SettingsSection
|
||||
title={t('Header navigation')}
|
||||
@@ -142,7 +190,7 @@ export function HeaderNavigationSection({
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className='space-y-6'>
|
||||
<div className='grid gap-4 md:grid-cols-2'>
|
||||
{modules.map((module) => (
|
||||
{simpleModules.map((module) => (
|
||||
<FormField
|
||||
key={module.key}
|
||||
control={form.control}
|
||||
@@ -168,59 +216,57 @@ export function HeaderNavigationSection({
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className='rounded-lg border p-4'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='pricingEnabled'
|
||||
render={({ field }) => (
|
||||
<FormItem className='flex flex-row items-start justify-between rounded-lg border p-4'>
|
||||
<div className='space-y-0.5 pe-4'>
|
||||
<FormLabel className='text-base'>
|
||||
{t('Models directory')}
|
||||
</FormLabel>
|
||||
<FormDescription>
|
||||
{t(
|
||||
'Exposes the pricing/models catalog in the top navigation.'
|
||||
)}
|
||||
</FormDescription>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className='grid gap-4 lg:grid-cols-2'>
|
||||
{accessModules.map((module) => (
|
||||
<div key={module.enabledKey} className='rounded-lg border p-4'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={module.enabledKey}
|
||||
render={({ field }) => (
|
||||
<FormItem className='flex flex-row items-start justify-between rounded-lg border p-4'>
|
||||
<div className='space-y-0.5 pe-4'>
|
||||
<FormLabel className='text-base'>
|
||||
{module.title}
|
||||
</FormLabel>
|
||||
<FormDescription>{module.description}</FormDescription>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='pricingRequireAuth'
|
||||
render={({ field }) => (
|
||||
<FormItem className='mt-4 flex flex-row items-start justify-between rounded-lg border border-dashed p-4'>
|
||||
<div className='space-y-0.5 pe-4'>
|
||||
<FormLabel className='text-base'>
|
||||
{t('Require login to view models')}
|
||||
</FormLabel>
|
||||
<FormDescription>
|
||||
{t(
|
||||
'Visitors must authenticate before accessing the pricing directory.'
|
||||
)}
|
||||
</FormDescription>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={!form.watch('pricingEnabled')}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={module.requireAuthKey}
|
||||
render={({ field }) => (
|
||||
<FormItem className='mt-4 flex flex-row items-start justify-between rounded-lg border border-dashed p-4'>
|
||||
<div className='space-y-0.5 pe-4'>
|
||||
<FormLabel className='text-base'>
|
||||
{module.requireAuthTitle}
|
||||
</FormLabel>
|
||||
<FormDescription>
|
||||
{module.requireAuthDescription}
|
||||
</FormDescription>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
disabled={!form.watch(module.requireAuthDependsOn)}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className='flex flex-wrap gap-3'>
|
||||
|
||||
@@ -269,11 +269,6 @@ function getModeBadgeVariant(
|
||||
return 'outline'
|
||||
}
|
||||
|
||||
function truncateExpr(value: string) {
|
||||
if (!value) return ''
|
||||
return value.length > 110 ? `${value.slice(0, 110)}...` : value
|
||||
}
|
||||
|
||||
function buildPreviewRows(
|
||||
values: ModelPricingFormValues,
|
||||
mode: PricingMode,
|
||||
|
||||
Reference in New Issue
Block a user