feat: MyEasyCMS v2 — Full SaaS rebuild
Complete rebuild of 22-year-old PHP CMS as modern SaaS: Database (15 migrations, 42+ tables): - Foundation: account_settings, audit_log, GDPR register, cms_files - Module Engine: modules, fields, records, permissions, relations + RPC - Members: 45+ field member profiles, departments, roles, honors, SEPA mandates - Courses: courses, sessions, categories, instructors, locations, attendance - Bookings: rooms, guests, bookings with availability - Events: events, registrations, holiday passes - Finance: SEPA batches/items (pain.008/001 XML), invoices - Newsletter: campaigns, templates, recipients, subscriptions - Site Builder: site_pages (Puck JSON), site_settings, cms_posts - Portal Auth: member_portal_invitations, user linking Feature Packages (9): - @kit/module-builder — dynamic low-code CRUD engine - @kit/member-management — 31 API methods, 21 actions, 8 components - @kit/course-management, @kit/booking-management, @kit/event-management - @kit/finance — SEPA XML generator + IBAN validator - @kit/newsletter — campaigns + dispatch - @kit/document-generator — PDF/Excel/Word - @kit/site-builder — Puck visual editor, 15 blocks, public rendering Pages (60+): - Dashboard with real stats from all APIs - Full CRUD for all 8 domains with react-hook-form + Zod - Recharts statistics - German i18n throughout - Member portal with auth + invitation system - Public club websites via Puck at /club/[slug] Infrastructure: - Dockerfile (multi-stage, standalone output) - docker-compose.yml (Supabase self-hosted + Next.js) - Kong API gateway config - .env.production.example
This commit is contained in:
@@ -1,19 +1,34 @@
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
import { ArrowRightIcon, LayoutDashboard } from 'lucide-react';
|
||||
import {
|
||||
ArrowRightIcon,
|
||||
BookOpenIcon,
|
||||
CalendarIcon,
|
||||
FileTextIcon,
|
||||
GraduationCapIcon,
|
||||
LayoutDashboardIcon,
|
||||
MailIcon,
|
||||
ShieldCheckIcon,
|
||||
UsersIcon,
|
||||
WalletIcon,
|
||||
BedDoubleIcon,
|
||||
GlobeIcon,
|
||||
ZapIcon,
|
||||
HeadsetIcon,
|
||||
LockIcon,
|
||||
SmartphoneIcon,
|
||||
CheckIcon,
|
||||
} from 'lucide-react';
|
||||
|
||||
import { PricingTable } from '@kit/billing-gateway/marketing';
|
||||
import {
|
||||
CtaButton,
|
||||
EcosystemShowcase,
|
||||
FeatureCard,
|
||||
FeatureGrid,
|
||||
FeatureShowcase,
|
||||
FeatureShowcaseIconContainer,
|
||||
Hero,
|
||||
Pill,
|
||||
PillActionButton,
|
||||
SecondaryHero,
|
||||
} from '@kit/ui/marketing';
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
@@ -23,31 +38,25 @@ import pathsConfig from '~/config/paths.config';
|
||||
|
||||
function Home() {
|
||||
return (
|
||||
<div className={'mt-4 flex flex-col space-y-24 py-14'}>
|
||||
<div className={'mt-4 flex flex-col space-y-24 py-14 lg:space-y-36'}>
|
||||
{/* Hero Section */}
|
||||
<div className={'mx-auto'}>
|
||||
<Hero
|
||||
pill={
|
||||
<Pill label={'New'}>
|
||||
<span>The SaaS Starter Kit for ambitious developers</span>
|
||||
<PillActionButton
|
||||
render={
|
||||
<Link href={'/auth/sign-up'}>
|
||||
<ArrowRightIcon className={'h-4 w-4'} />
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
<Pill label={'Neu'}>
|
||||
<span>
|
||||
<Trans i18nKey={'marketing.heroPill'} />
|
||||
</span>
|
||||
</Pill>
|
||||
}
|
||||
title={
|
||||
<span className="text-secondary-foreground">
|
||||
<span>Ship a SaaS faster than ever.</span>
|
||||
<Trans i18nKey={'marketing.heroTitle'} />
|
||||
</span>
|
||||
}
|
||||
subtitle={
|
||||
<span>
|
||||
Makerkit gives you a production-ready boilerplate to build your
|
||||
SaaS faster than ever before with the next-gen SaaS Starter Kit.
|
||||
Get started in minutes.
|
||||
<Trans i18nKey={'marketing.heroSubtitle'} />
|
||||
</span>
|
||||
}
|
||||
cta={<MainCallToActionButton />}
|
||||
@@ -55,95 +64,227 @@ function Home() {
|
||||
<Image
|
||||
priority
|
||||
className={
|
||||
'dark:border-primary/10 w-full rounded-lg border border-gray-200'
|
||||
'dark:border-primary/10 w-full rounded-2xl border border-gray-200 shadow-2xl'
|
||||
}
|
||||
width={3558}
|
||||
height={2222}
|
||||
src={`/images/dashboard.webp`}
|
||||
alt={`App Image`}
|
||||
alt={`MyEasyCMS Dashboard`}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Trust Indicators */}
|
||||
<div className={'container mx-auto'}>
|
||||
<div className="flex flex-col items-center gap-8">
|
||||
<p className="text-muted-foreground text-sm font-medium uppercase tracking-widest">
|
||||
<Trans i18nKey={'marketing.trustedBy'} />
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap items-center justify-center gap-x-12 gap-y-6">
|
||||
<TrustItem icon={UsersIcon} label="marketing.trustAssociations" />
|
||||
<TrustItem
|
||||
icon={GraduationCapIcon}
|
||||
label="marketing.trustSchools"
|
||||
/>
|
||||
<TrustItem icon={BookOpenIcon} label="marketing.trustClubs" />
|
||||
<TrustItem
|
||||
icon={GlobeIcon}
|
||||
label="marketing.trustOrganizations"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Core Modules Feature Grid */}
|
||||
<div className={'container mx-auto'}>
|
||||
<div className={'py-4 xl:py-8'}>
|
||||
<FeatureShowcase
|
||||
heading={
|
||||
<>
|
||||
<b className="font-medium tracking-tight dark:text-white">
|
||||
The ultimate SaaS Starter Kit
|
||||
<Trans i18nKey={'marketing.featuresHeading'} />
|
||||
</b>
|
||||
.{' '}
|
||||
<span className="text-secondary-foreground/70 block font-normal tracking-tight">
|
||||
Unleash your creativity and build your SaaS faster than ever
|
||||
with Makerkit.
|
||||
<Trans i18nKey={'marketing.featuresSubheading'} />
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
icon={
|
||||
<FeatureShowcaseIconContainer>
|
||||
<LayoutDashboard className="h-4 w-4" />
|
||||
<span>All-in-one solution</span>
|
||||
<LayoutDashboardIcon className="h-4 w-4" />
|
||||
<span>
|
||||
<Trans i18nKey={'marketing.featuresLabel'} />
|
||||
</span>
|
||||
</FeatureShowcaseIconContainer>
|
||||
}
|
||||
>
|
||||
<FeatureGrid>
|
||||
<FeatureCard
|
||||
className={'relative col-span-1 overflow-hidden'}
|
||||
label={'Beautiful Dashboard'}
|
||||
description={`Makerkit provides a beautiful dashboard to manage your SaaS business.`}
|
||||
></FeatureCard>
|
||||
|
||||
<FeatureCard
|
||||
className={'relative col-span-1 w-full overflow-hidden'}
|
||||
label={'Authentication'}
|
||||
description={`Makerkit provides a variety of providers to allow your users to sign in.`}
|
||||
></FeatureCard>
|
||||
|
||||
<FeatureCard
|
||||
className={'relative col-span-1 overflow-hidden'}
|
||||
label={'Multi Tenancy'}
|
||||
description={`Multi tenant memberships for your SaaS business.`}
|
||||
<div className="mt-2 grid w-full grid-cols-1 gap-4 md:mt-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
<IconFeatureCard
|
||||
icon={UsersIcon}
|
||||
titleKey="marketing.featureMembersTitle"
|
||||
descKey="marketing.featureMembersDesc"
|
||||
/>
|
||||
|
||||
<FeatureCard
|
||||
className={'relative col-span-1 overflow-hidden'}
|
||||
label={'Billing'}
|
||||
description={`Makerkit supports multiple payment gateways to charge your customers.`}
|
||||
<IconFeatureCard
|
||||
icon={GraduationCapIcon}
|
||||
titleKey="marketing.featureCoursesTitle"
|
||||
descKey="marketing.featureCoursesDesc"
|
||||
/>
|
||||
|
||||
<FeatureCard
|
||||
className={'relative col-span-1 overflow-hidden'}
|
||||
label={'Plugins'}
|
||||
description={`Extend your SaaS with plugins that you can install using the CLI.`}
|
||||
<IconFeatureCard
|
||||
icon={BedDoubleIcon}
|
||||
titleKey="marketing.featureBookingsTitle"
|
||||
descKey="marketing.featureBookingsDesc"
|
||||
/>
|
||||
|
||||
<FeatureCard
|
||||
className={'relative col-span-1 overflow-hidden'}
|
||||
label={'Documentation'}
|
||||
description={`Makerkit provides a comprehensive documentation to help you get started.`}
|
||||
<IconFeatureCard
|
||||
icon={CalendarIcon}
|
||||
titleKey="marketing.featureEventsTitle"
|
||||
descKey="marketing.featureEventsDesc"
|
||||
/>
|
||||
</FeatureGrid>
|
||||
<IconFeatureCard
|
||||
icon={WalletIcon}
|
||||
titleKey="marketing.featureFinanceTitle"
|
||||
descKey="marketing.featureFinanceDesc"
|
||||
/>
|
||||
<IconFeatureCard
|
||||
icon={MailIcon}
|
||||
titleKey="marketing.featureNewsletterTitle"
|
||||
descKey="marketing.featureNewsletterDesc"
|
||||
/>
|
||||
</div>
|
||||
</FeatureShowcase>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Dashboard Showcase */}
|
||||
<div className={'container mx-auto'}>
|
||||
<EcosystemShowcase
|
||||
heading="The ultimate SaaS Starter Kit for founders."
|
||||
description="Unleash your creativity and build your SaaS faster than ever with Makerkit. Get started in minutes and ship your SaaS in no time."
|
||||
heading={<Trans i18nKey={'marketing.showcaseHeading'} />}
|
||||
description={<Trans i18nKey={'marketing.showcaseDescription'} />}
|
||||
>
|
||||
<Image
|
||||
className="rounded-md"
|
||||
src={'/images/sign-in.webp'}
|
||||
alt="Sign in"
|
||||
width={1000}
|
||||
height={1000}
|
||||
className="rounded-lg shadow-lg"
|
||||
src={'/images/dashboard.webp'}
|
||||
alt="MyEasyCMS Dashboard"
|
||||
width={1200}
|
||||
height={800}
|
||||
/>
|
||||
</EcosystemShowcase>
|
||||
</div>
|
||||
|
||||
{/* Additional Features Row */}
|
||||
<div className={'container mx-auto'}>
|
||||
<div className={'py-4 xl:py-8'}>
|
||||
<FeatureShowcase
|
||||
heading={
|
||||
<>
|
||||
<b className="font-medium tracking-tight dark:text-white">
|
||||
<Trans i18nKey={'marketing.additionalFeaturesHeading'} />
|
||||
</b>
|
||||
.{' '}
|
||||
<span className="text-secondary-foreground/70 block font-normal tracking-tight">
|
||||
<Trans
|
||||
i18nKey={'marketing.additionalFeaturesSubheading'}
|
||||
/>
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
icon={
|
||||
<FeatureShowcaseIconContainer>
|
||||
<ZapIcon className="h-4 w-4" />
|
||||
<span>
|
||||
<Trans i18nKey={'marketing.additionalFeaturesLabel'} />
|
||||
</span>
|
||||
</FeatureShowcaseIconContainer>
|
||||
}
|
||||
>
|
||||
<div className="mt-2 grid w-full grid-cols-1 gap-4 md:mt-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
<IconFeatureCard
|
||||
icon={FileTextIcon}
|
||||
titleKey="marketing.featureDocumentsTitle"
|
||||
descKey="marketing.featureDocumentsDesc"
|
||||
/>
|
||||
<IconFeatureCard
|
||||
icon={GlobeIcon}
|
||||
titleKey="marketing.featureSiteBuilderTitle"
|
||||
descKey="marketing.featureSiteBuilderDesc"
|
||||
/>
|
||||
<IconFeatureCard
|
||||
icon={LayoutDashboardIcon}
|
||||
titleKey="marketing.featureModulesTitle"
|
||||
descKey="marketing.featureModulesDesc"
|
||||
/>
|
||||
</div>
|
||||
</FeatureShowcase>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Why Choose Us Section */}
|
||||
<div className={'container mx-auto'}>
|
||||
<EcosystemShowcase
|
||||
heading={<Trans i18nKey={'marketing.whyChooseHeading'} />}
|
||||
description={<Trans i18nKey={'marketing.whyChooseDescription'} />}
|
||||
textPosition="right"
|
||||
>
|
||||
<div className="flex flex-col gap-6">
|
||||
<WhyItem
|
||||
icon={SmartphoneIcon}
|
||||
titleKey="marketing.whyResponsiveTitle"
|
||||
descKey="marketing.whyResponsiveDesc"
|
||||
/>
|
||||
<WhyItem
|
||||
icon={LockIcon}
|
||||
titleKey="marketing.whySecureTitle"
|
||||
descKey="marketing.whySecureDesc"
|
||||
/>
|
||||
<WhyItem
|
||||
icon={HeadsetIcon}
|
||||
titleKey="marketing.whySupportTitle"
|
||||
descKey="marketing.whySupportDesc"
|
||||
/>
|
||||
<WhyItem
|
||||
icon={ShieldCheckIcon}
|
||||
titleKey="marketing.whyGdprTitle"
|
||||
descKey="marketing.whyGdprDesc"
|
||||
/>
|
||||
</div>
|
||||
</EcosystemShowcase>
|
||||
</div>
|
||||
|
||||
{/* How It Works */}
|
||||
<div className="container mx-auto">
|
||||
<div className="flex flex-col items-center gap-12">
|
||||
<div className="text-center">
|
||||
<h2 className="text-3xl font-medium tracking-tight dark:text-white xl:text-5xl">
|
||||
<Trans i18nKey={'marketing.howItWorksHeading'} />
|
||||
</h2>
|
||||
<p className="text-secondary-foreground/70 mx-auto mt-4 max-w-2xl text-xl font-medium tracking-tight">
|
||||
<Trans i18nKey={'marketing.howItWorksSubheading'} />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid w-full grid-cols-1 gap-8 md:grid-cols-3">
|
||||
<StepCard
|
||||
step="01"
|
||||
titleKey="marketing.howStep1Title"
|
||||
descKey="marketing.howStep1Desc"
|
||||
/>
|
||||
<StepCard
|
||||
step="02"
|
||||
titleKey="marketing.howStep2Title"
|
||||
descKey="marketing.howStep2Desc"
|
||||
/>
|
||||
<StepCard
|
||||
step="03"
|
||||
titleKey="marketing.howStep3Title"
|
||||
descKey="marketing.howStep3Desc"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pricing Section */}
|
||||
<div className={'container mx-auto'}>
|
||||
<div
|
||||
className={
|
||||
@@ -151,9 +292,13 @@ function Home() {
|
||||
}
|
||||
>
|
||||
<SecondaryHero
|
||||
pill={<Pill label="Start for free">No credit card required.</Pill>}
|
||||
heading="Fair pricing for all types of businesses"
|
||||
subheading="Get started on our free plan and upgrade when you are ready."
|
||||
pill={
|
||||
<Pill label={<Trans i18nKey={'marketing.pricingPillLabel'} />}>
|
||||
<Trans i18nKey={'marketing.pricingPillText'} />
|
||||
</Pill>
|
||||
}
|
||||
heading={<Trans i18nKey={'marketing.pricingHeading'} />}
|
||||
subheading={<Trans i18nKey={'marketing.pricingSubheading'} />}
|
||||
/>
|
||||
|
||||
<div className={'w-full'}>
|
||||
@@ -167,6 +312,37 @@ function Home() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Final CTA */}
|
||||
<div className="container mx-auto">
|
||||
<div className="bg-primary/5 flex flex-col items-center gap-8 rounded-2xl border p-12 text-center lg:p-16">
|
||||
<h2 className="max-w-3xl text-3xl font-medium tracking-tight dark:text-white xl:text-5xl">
|
||||
<Trans i18nKey={'marketing.ctaHeading'} />
|
||||
</h2>
|
||||
<p className="text-secondary-foreground/70 max-w-2xl text-lg">
|
||||
<Trans i18nKey={'marketing.ctaDescription'} />
|
||||
</p>
|
||||
<div className="flex flex-col gap-3 sm:flex-row">
|
||||
<CtaButton className="h-12 px-8 text-base">
|
||||
<Link href={'/auth/sign-up'}>
|
||||
<span className="flex items-center gap-2">
|
||||
<Trans i18nKey={'marketing.ctaButtonPrimary'} />
|
||||
<ArrowRightIcon className="h-4 w-4" />
|
||||
</span>
|
||||
</Link>
|
||||
</CtaButton>
|
||||
<CtaButton variant={'outline'} className="h-12 px-8 text-base">
|
||||
<Link href={'/contact'}>
|
||||
<Trans i18nKey={'marketing.ctaButtonSecondary'} />
|
||||
</Link>
|
||||
</CtaButton>
|
||||
</div>
|
||||
<p className="text-muted-foreground flex items-center gap-2 text-sm">
|
||||
<CheckIcon className="h-4 w-4" />
|
||||
<Trans i18nKey={'marketing.ctaNote'} />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -201,3 +377,73 @@ function MainCallToActionButton() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function IconFeatureCard(props: {
|
||||
icon: React.ComponentType<{ className?: string }>;
|
||||
titleKey: string;
|
||||
descKey: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="bg-muted/50 flex flex-col gap-3 rounded p-6">
|
||||
<div className="bg-primary/10 flex h-10 w-10 items-center justify-center rounded-lg">
|
||||
<props.icon className="text-primary h-5 w-5" />
|
||||
</div>
|
||||
<h4 className="text-lg font-medium">
|
||||
<Trans i18nKey={props.titleKey} />
|
||||
</h4>
|
||||
<p className="text-muted-foreground max-w-xs text-sm">
|
||||
<Trans i18nKey={props.descKey} />
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TrustItem(props: {
|
||||
icon: React.ComponentType<{ className?: string }>;
|
||||
label: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="text-muted-foreground flex items-center gap-2.5">
|
||||
<props.icon className="h-5 w-5" />
|
||||
<span className="text-sm font-medium">
|
||||
<Trans i18nKey={props.label} />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function WhyItem(props: {
|
||||
icon: React.ComponentType<{ className?: string }>;
|
||||
titleKey: string;
|
||||
descKey: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex gap-4">
|
||||
<div className="bg-primary/10 flex h-10 w-10 shrink-0 items-center justify-center rounded-lg">
|
||||
<props.icon className="text-primary h-5 w-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-secondary-foreground font-medium">
|
||||
<Trans i18nKey={props.titleKey} />
|
||||
</h4>
|
||||
<p className="text-muted-foreground mt-1 text-sm">
|
||||
<Trans i18nKey={props.descKey} />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StepCard(props: { step: string; titleKey: string; descKey: string }) {
|
||||
return (
|
||||
<div className="bg-muted/50 relative flex flex-col gap-4 rounded-lg p-6">
|
||||
<span className="text-primary/20 text-6xl font-bold">{props.step}</span>
|
||||
<h3 className="text-secondary-foreground text-xl font-medium">
|
||||
<Trans i18nKey={props.titleKey} />
|
||||
</h3>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
<Trans i18nKey={props.descKey} />
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user