Initial state for GitNexus analysis
This commit is contained in:
97
apps/web/config/billing-plans.config.ts
Normal file
97
apps/web/config/billing-plans.config.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* CMS Billing Plans — Stripe product configuration
|
||||
* Phase 12: Three tiers for the SaaS product
|
||||
*/
|
||||
|
||||
export const CMS_BILLING_PLANS = {
|
||||
starter: {
|
||||
name: 'Starter',
|
||||
description: 'Für kleine Vereine und Organisationen',
|
||||
limits: {
|
||||
modules: 1,
|
||||
records: 100,
|
||||
members: 50,
|
||||
storageGb: 1,
|
||||
users: 3,
|
||||
},
|
||||
features: [
|
||||
'Module Builder (1 Modul)',
|
||||
'Mitgliederverwaltung (bis 50)',
|
||||
'Basisfunktionen',
|
||||
'Deutsche Oberfläche',
|
||||
'E-Mail-Support',
|
||||
],
|
||||
pricing: {
|
||||
monthly: { amount: 1900, currency: 'eur' }, // 19,00 €
|
||||
yearly: { amount: 19000, currency: 'eur' }, // 190,00 € (2 Monate gratis)
|
||||
},
|
||||
},
|
||||
|
||||
professional: {
|
||||
name: 'Professional',
|
||||
description: 'Für aktive Vereine und Volkshochschulen',
|
||||
limits: {
|
||||
modules: -1, // unlimited
|
||||
records: -1,
|
||||
members: -1,
|
||||
storageGb: 10,
|
||||
users: 20,
|
||||
},
|
||||
features: [
|
||||
'Unbegrenzte Module',
|
||||
'Unbegrenzte Mitglieder',
|
||||
'Kursverwaltung',
|
||||
'SEPA-Lastschriften',
|
||||
'Newsletter',
|
||||
'Dokumentengenerierung',
|
||||
'Import/Export',
|
||||
'Prioritäts-Support',
|
||||
],
|
||||
pricing: {
|
||||
monthly: { amount: 4900, currency: 'eur' }, // 49,00 €
|
||||
yearly: { amount: 49000, currency: 'eur' }, // 490,00 €
|
||||
},
|
||||
},
|
||||
|
||||
enterprise: {
|
||||
name: 'Enterprise',
|
||||
description: 'Für große Organisationen mit individuellen Anforderungen',
|
||||
limits: {
|
||||
modules: -1,
|
||||
records: -1,
|
||||
members: -1,
|
||||
storageGb: 100,
|
||||
users: -1,
|
||||
},
|
||||
features: [
|
||||
'Alles aus Professional',
|
||||
'White-Label / eigenes Branding',
|
||||
'REST-API Zugang',
|
||||
'Multi-Mandanten-Verwaltung',
|
||||
'Dedizierte Instanz (optional)',
|
||||
'Individuelle Anpassungen',
|
||||
'Persönlicher Ansprechpartner',
|
||||
'SLA-Garantie',
|
||||
],
|
||||
pricing: {
|
||||
monthly: { amount: 14900, currency: 'eur' }, // 149,00 €
|
||||
yearly: { amount: 149000, currency: 'eur' }, // 1.490,00 €
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type BillingPlanKey = keyof typeof CMS_BILLING_PLANS;
|
||||
|
||||
/**
|
||||
* Check if a plan allows a given feature/limit.
|
||||
*/
|
||||
export function checkPlanLimit(
|
||||
plan: BillingPlanKey,
|
||||
limit: keyof typeof CMS_BILLING_PLANS.starter.limits,
|
||||
currentUsage: number,
|
||||
): boolean {
|
||||
const planLimits = CMS_BILLING_PLANS[plan].limits;
|
||||
const max = planLimits[limit];
|
||||
if (max === -1) return true; // unlimited
|
||||
return currentUsage < max;
|
||||
}
|
||||
@@ -41,6 +41,15 @@ const FeatureFlagsSchema = z.object({
|
||||
enableTeamsOnly: z.boolean({
|
||||
error: 'Provide the variable NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_ONLY',
|
||||
}),
|
||||
// CMS feature flags
|
||||
enableModuleBuilder: z.boolean().default(true),
|
||||
enableMemberManagement: z.boolean().default(true),
|
||||
enableCourseManagement: z.boolean().default(true),
|
||||
enableBookingManagement: z.boolean().default(false),
|
||||
enableSepaPayments: z.boolean().default(true),
|
||||
enableDocumentGeneration: z.boolean().default(true),
|
||||
enableNewsletter: z.boolean().default(true),
|
||||
enableGdprCompliance: z.boolean().default(true),
|
||||
});
|
||||
|
||||
const featuresFlagConfig = FeatureFlagsSchema.parse({
|
||||
@@ -90,6 +99,39 @@ const featuresFlagConfig = FeatureFlagsSchema.parse({
|
||||
process.env.NEXT_PUBLIC_ENABLE_TEAM_ACCOUNTS_ONLY,
|
||||
false,
|
||||
),
|
||||
// CMS feature flags
|
||||
enableModuleBuilder: getBoolean(
|
||||
process.env.NEXT_PUBLIC_ENABLE_MODULE_BUILDER,
|
||||
true,
|
||||
),
|
||||
enableMemberManagement: getBoolean(
|
||||
process.env.NEXT_PUBLIC_ENABLE_MEMBER_MANAGEMENT,
|
||||
true,
|
||||
),
|
||||
enableCourseManagement: getBoolean(
|
||||
process.env.NEXT_PUBLIC_ENABLE_COURSE_MANAGEMENT,
|
||||
true,
|
||||
),
|
||||
enableBookingManagement: getBoolean(
|
||||
process.env.NEXT_PUBLIC_ENABLE_BOOKING_MANAGEMENT,
|
||||
false,
|
||||
),
|
||||
enableSepaPayments: getBoolean(
|
||||
process.env.NEXT_PUBLIC_ENABLE_SEPA_PAYMENTS,
|
||||
true,
|
||||
),
|
||||
enableDocumentGeneration: getBoolean(
|
||||
process.env.NEXT_PUBLIC_ENABLE_DOCUMENT_GENERATION,
|
||||
true,
|
||||
),
|
||||
enableNewsletter: getBoolean(
|
||||
process.env.NEXT_PUBLIC_ENABLE_NEWSLETTER,
|
||||
true,
|
||||
),
|
||||
enableGdprCompliance: getBoolean(
|
||||
process.env.NEXT_PUBLIC_ENABLE_GDPR_COMPLIANCE,
|
||||
true,
|
||||
),
|
||||
} satisfies z.output<typeof FeatureFlagsSchema>);
|
||||
|
||||
export default featuresFlagConfig;
|
||||
|
||||
@@ -22,6 +22,14 @@ const PathsSchema = z.object({
|
||||
accountProfileSettings: z.string().min(1),
|
||||
createTeam: z.string().min(1),
|
||||
joinTeam: z.string().min(1),
|
||||
// CMS paths
|
||||
accountModules: z.string().min(1),
|
||||
accountCmsMembers: z.string().min(1),
|
||||
accountCourses: z.string().min(1),
|
||||
accountBookings: z.string().min(1),
|
||||
accountFinance: z.string().min(1),
|
||||
accountDocuments: z.string().min(1),
|
||||
accountNewsletter: z.string().min(1),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -47,6 +55,14 @@ const pathsConfig = PathsSchema.parse({
|
||||
accountProfileSettings: `/home/[account]/settings/profile`,
|
||||
createTeam: '/home/create-team',
|
||||
joinTeam: '/join',
|
||||
// CMS paths
|
||||
accountModules: `/home/[account]/modules`,
|
||||
accountCmsMembers: `/home/[account]/members-cms`,
|
||||
accountCourses: `/home/[account]/courses`,
|
||||
accountBookings: `/home/[account]/bookings`,
|
||||
accountFinance: `/home/[account]/finance`,
|
||||
accountDocuments: `/home/[account]/documents`,
|
||||
accountNewsletter: `/home/[account]/newsletter`,
|
||||
},
|
||||
} satisfies z.output<typeof PathsSchema>);
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { CreditCard, LayoutDashboard, Settings, Users } from 'lucide-react';
|
||||
import {
|
||||
CreditCard, LayoutDashboard, Settings, Users, Database,
|
||||
UserCheck, GraduationCap, Hotel, Calendar, Wallet,
|
||||
FileText, Mail,
|
||||
} from 'lucide-react';
|
||||
|
||||
import { NavigationConfigSchema } from '@kit/ui/navigation-schema';
|
||||
|
||||
@@ -17,7 +21,61 @@ const getRoutes = (account: string) => [
|
||||
Icon: <LayoutDashboard className={iconClasses} />,
|
||||
highlightMatch: `${pathsConfig.app.home}$`,
|
||||
},
|
||||
],
|
||||
featureFlagsConfig.enableModuleBuilder
|
||||
? {
|
||||
label: 'common.routes.modules',
|
||||
path: createPath(pathsConfig.app.accountModules, account),
|
||||
Icon: <Database className={iconClasses} />,
|
||||
}
|
||||
: undefined,
|
||||
featureFlagsConfig.enableMemberManagement
|
||||
? {
|
||||
label: 'common.routes.cmsMembers',
|
||||
path: createPath(pathsConfig.app.accountCmsMembers, account),
|
||||
Icon: <UserCheck className={iconClasses} />,
|
||||
}
|
||||
: undefined,
|
||||
featureFlagsConfig.enableCourseManagement
|
||||
? {
|
||||
label: 'common.routes.courses',
|
||||
path: createPath(pathsConfig.app.accountCourses, account),
|
||||
Icon: <GraduationCap className={iconClasses} />,
|
||||
}
|
||||
: undefined,
|
||||
featureFlagsConfig.enableBookingManagement
|
||||
? {
|
||||
label: 'common.routes.bookings',
|
||||
path: createPath(pathsConfig.app.accountBookings, account),
|
||||
Icon: <Hotel className={iconClasses} />,
|
||||
}
|
||||
: undefined,
|
||||
{
|
||||
label: 'common.routes.events',
|
||||
path: createPath(`/home/[account]/events`, account),
|
||||
Icon: <Calendar className={iconClasses} />,
|
||||
},
|
||||
featureFlagsConfig.enableSepaPayments
|
||||
? {
|
||||
label: 'common.routes.finance',
|
||||
path: createPath(pathsConfig.app.accountFinance, account),
|
||||
Icon: <Wallet className={iconClasses} />,
|
||||
}
|
||||
: undefined,
|
||||
featureFlagsConfig.enableDocumentGeneration
|
||||
? {
|
||||
label: 'common.routes.documents',
|
||||
path: createPath(pathsConfig.app.accountDocuments, account),
|
||||
Icon: <FileText className={iconClasses} />,
|
||||
}
|
||||
: undefined,
|
||||
featureFlagsConfig.enableNewsletter
|
||||
? {
|
||||
label: 'common.routes.newsletter',
|
||||
path: createPath(pathsConfig.app.accountNewsletter, account),
|
||||
Icon: <Mail className={iconClasses} />,
|
||||
}
|
||||
: undefined,
|
||||
].filter(Boolean),
|
||||
},
|
||||
{
|
||||
label: 'common.routes.settings',
|
||||
|
||||
Reference in New Issue
Block a user