From 0901c7b762cccbcd1939a44351e99658bb98be43 Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Sun, 16 Nov 2025 23:24:40 +0100 Subject: [PATCH 1/5] Allow routes to opt out of AuthSyncModal via context --- .../infrastructure/auth/AuthSyncModal.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/application/shared-webapp/infrastructure/auth/AuthSyncModal.tsx b/application/shared-webapp/infrastructure/auth/AuthSyncModal.tsx index 8a4a753eb8..df8f8fcae1 100644 --- a/application/shared-webapp/infrastructure/auth/AuthSyncModal.tsx +++ b/application/shared-webapp/infrastructure/auth/AuthSyncModal.tsx @@ -1,3 +1,4 @@ +import { useRouterState } from "@tanstack/react-router"; import type { ComponentType } from "react"; import { useAuthSync } from "./useAuthSync"; @@ -17,11 +18,20 @@ interface AuthSyncRendererProps { * Component that renders the authentication sync modal when needed. * Place this as a sibling to your main app content, not as a wrapper. * The modalComponent must be provided by the consuming application to avoid circular dependencies. + * + * Routes can opt out by adding: beforeLoad: () => ({ disableAuthSync: true }) + * This is useful for public pages that should remain standalone. */ export function AuthSyncModal({ modalComponent: ModalComponent }: AuthSyncRendererProps) { + const routerState = useRouterState(); const { modalState, handlePrimaryAction } = useAuthSync(); - if (!modalState.isOpen) { + // Check if current route opts out of auth sync modal + const currentRoute = routerState.matches.at(-1); + const disableAuthSync = + (currentRoute?.context as { disableAuthSync?: boolean } | undefined)?.disableAuthSync ?? false; + + if (disableAuthSync || !modalState.isOpen) { return null; } From 8e161abf8c18cf33e2ef2ddd62187490b0fe73fc Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Sun, 16 Nov 2025 15:42:47 +0100 Subject: [PATCH 2/5] Redesign landing page with minimalistic approach --- .../(index)/-components/CommunitySection.tsx | 72 --------- .../routes/(index)/-components/CtaSection.tsx | 18 --- .../(index)/-components/CtaSection2.tsx | 22 --- .../(index)/-components/CtaSection3.tsx | 21 --- .../(index)/-components/FeatureSection.tsx | 99 ------------ .../(index)/-components/FeatureSection2.tsx | 54 ------- .../(index)/-components/FeatureSection3.tsx | 76 --------- .../(index)/-components/FeatureSection4.tsx | 60 -------- .../(index)/-components/FooterSection.tsx | 45 ------ .../(index)/-components/HeroSection.tsx | 145 ------------------ .../(index)/-components/TechnologySection.tsx | 71 --------- .../-components/TechnologySection2.tsx | 72 --------- .../routes/(index)/-components/cdnImages.ts | 28 ---- .../WebApp/routes/(index)/index.tsx | 62 ++++---- .../WebApp/shared/components/PublicFooter.tsx | 113 ++++++++++++++ .../shared/components/PublicNavigation.tsx | 77 ++++++++++ .../shared/translations/locale/da-DK.po | 27 ++++ .../shared/translations/locale/en-US.po | 27 ++++ .../SinglePageAppConfiguration.cs | 3 +- 19 files changed, 274 insertions(+), 818 deletions(-) delete mode 100644 application/account-management/WebApp/routes/(index)/-components/CommunitySection.tsx delete mode 100644 application/account-management/WebApp/routes/(index)/-components/CtaSection.tsx delete mode 100644 application/account-management/WebApp/routes/(index)/-components/CtaSection2.tsx delete mode 100644 application/account-management/WebApp/routes/(index)/-components/CtaSection3.tsx delete mode 100644 application/account-management/WebApp/routes/(index)/-components/FeatureSection.tsx delete mode 100644 application/account-management/WebApp/routes/(index)/-components/FeatureSection2.tsx delete mode 100644 application/account-management/WebApp/routes/(index)/-components/FeatureSection3.tsx delete mode 100644 application/account-management/WebApp/routes/(index)/-components/FeatureSection4.tsx delete mode 100644 application/account-management/WebApp/routes/(index)/-components/FooterSection.tsx delete mode 100644 application/account-management/WebApp/routes/(index)/-components/HeroSection.tsx delete mode 100644 application/account-management/WebApp/routes/(index)/-components/TechnologySection.tsx delete mode 100644 application/account-management/WebApp/routes/(index)/-components/TechnologySection2.tsx delete mode 100644 application/account-management/WebApp/routes/(index)/-components/cdnImages.ts create mode 100644 application/account-management/WebApp/shared/components/PublicFooter.tsx create mode 100644 application/account-management/WebApp/shared/components/PublicNavigation.tsx diff --git a/application/account-management/WebApp/routes/(index)/-components/CommunitySection.tsx b/application/account-management/WebApp/routes/(index)/-components/CommunitySection.tsx deleted file mode 100644 index 14e5e6687d..0000000000 --- a/application/account-management/WebApp/routes/(index)/-components/CommunitySection.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { Link } from "@repo/ui/components/Link"; -import { ArrowRight } from "lucide-react"; -import type { LinkProps } from "react-aria-components"; -import { githubImageUrl, slackImageUrl, youtubeImageUrl } from "./cdnImages"; - -// Props for FeatureBlock component -interface FeatureBlockProps { - title: string; - content: string; - image: string; - href: LinkProps["href"]; - linkText: string; - arrow?: boolean; -} - -// Component to display a feature with an image, title, content, and a link -function FeatureBlock({ title, content, image, href, linkText, arrow }: Readonly) { - return ( -
-
- {title} -
-
{title}
-
{content}
- - {linkText} {arrow && } - -
- ); -} - -// Component for the Community Section -export function CommunitySection() { - return ( -
-
-

COMMUNITY

-

Join builders on PlatformPlatform

-

- Our community is full of developers, designers and founders - just like you, to get your going, sharing ideas - and experiences. Join us now. -

-
-
- - - -
-
- ); -} diff --git a/application/account-management/WebApp/routes/(index)/-components/CtaSection.tsx b/application/account-management/WebApp/routes/(index)/-components/CtaSection.tsx deleted file mode 100644 index 1855caa33e..0000000000 --- a/application/account-management/WebApp/routes/(index)/-components/CtaSection.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { Button } from "@repo/ui/components/Button"; - -// CtaSection: A functional component that displays a call to action -export function CtaSection() { - return ( -
-
-

A single solution for you to build on

-

- Join Startups and Enterprises already building on PlatformPlatform -

-
- -
-
-
- ); -} diff --git a/application/account-management/WebApp/routes/(index)/-components/CtaSection2.tsx b/application/account-management/WebApp/routes/(index)/-components/CtaSection2.tsx deleted file mode 100644 index 4e1af28e7a..0000000000 --- a/application/account-management/WebApp/routes/(index)/-components/CtaSection2.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { Button } from "@repo/ui/components/Button"; -import { calenderMockupUrl } from "./cdnImages"; - -// CtaSection2: A functional component that displays the second call to action section -export function CtaSection2() { - return ( -
-
-
-
- Get lightyears ahead and get your product in the hands of your customers -
-
No credit cards or hidden fees. Just Open Source.
- -
-
- Create Account -
-
-
- ); -} diff --git a/application/account-management/WebApp/routes/(index)/-components/CtaSection3.tsx b/application/account-management/WebApp/routes/(index)/-components/CtaSection3.tsx deleted file mode 100644 index 5a3ee4ec25..0000000000 --- a/application/account-management/WebApp/routes/(index)/-components/CtaSection3.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { SignUpButton } from "@repo/infrastructure/auth/SignUpButton"; - -// CtaSection3: A functional component that displays a call to action -export function CtaSection3() { - return ( -
-
-
-

Start scaling your business today

-

- Join Startups and Enterprises already building on PlatformPlatform -

-
- {/* Button component is used to display a call to action */} - Get started for free -
-
-
-
- ); -} diff --git a/application/account-management/WebApp/routes/(index)/-components/FeatureSection.tsx b/application/account-management/WebApp/routes/(index)/-components/FeatureSection.tsx deleted file mode 100644 index b90c51170a..0000000000 --- a/application/account-management/WebApp/routes/(index)/-components/FeatureSection.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { - accountSettingsUrl, - createAccountUrl, - darkModeUrl, - editProfileUrl, - enterCodeUrl, - lightModeUrl, - profileEditDarkUrl, - profileEditLightUrl, - usersUrl -} from "./cdnImages"; - -// FeatureSection: A functional component that displays the feature section -export function FeatureSection() { - return ( -
-
-

FEATURES

-

- All-in-one Infrastructure for any business -

-

- Get a full enterprise grade production environment running within 1 hour. From development, infrastructure as - code to fully deployed production application with a security score of 100%. -

-
-
-
- Create Account - Enter Code -
-
-
- Profile Edit Dark -
- Dark Mode -
-
- Light Mode -
-
- Edit Profile -
-
- Account Settings - Edit Profile Light -
-
- Users -
-
-
- ); -} diff --git a/application/account-management/WebApp/routes/(index)/-components/FeatureSection2.tsx b/application/account-management/WebApp/routes/(index)/-components/FeatureSection2.tsx deleted file mode 100644 index c1856146ce..0000000000 --- a/application/account-management/WebApp/routes/(index)/-components/FeatureSection2.tsx +++ /dev/null @@ -1,54 +0,0 @@ -// FeatureSection2: Displays a section with features -export function FeatureSection2() { - return ( -
-
-

FEATURES

- All the features you need to build anything you want -
-
-
- - - - - - -
-
-
- ); -} - -interface FeatureBlockProps { - title: string; - content: string; -} - -// FeatureBlock: Displays a single feature -function FeatureBlock({ title, content }: Readonly) { - return ( -
-
{title}
-
{content}
-
- ); -} diff --git a/application/account-management/WebApp/routes/(index)/-components/FeatureSection3.tsx b/application/account-management/WebApp/routes/(index)/-components/FeatureSection3.tsx deleted file mode 100644 index 96633f37f6..0000000000 --- a/application/account-management/WebApp/routes/(index)/-components/FeatureSection3.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import ThemeModeSelector from "@/federated-modules/common/ThemeModeSelector"; -import { createAccountUrl } from "./cdnImages"; - -// FeatureSection3: A functional component that displays the third feature section -export function FeatureSection3() { - return ( -
-
- -
-
-
- -
-
- Mockup -
-
-
- ); -} - -// FeatureTitle: A functional component that displays the title of the feature section -function FeatureTitle() { - return ( - <> -

FEATURES

-

- Focus on building and launching your core product. We got your back - Enterprise grade -

-

- PlatformPlatform uses best practices and enterprise-grade tech to provide the foundation you need to build your - core product. -

- - ); -} - -// FeatureList: A functional component that displays a list of features -function FeatureList() { - return ( - <> -
-

Sign up and Log in - built right in

-

- We’ve built user authentication and authorisation right in, so you don’t have to. With enterprise security - grade and using best practice. -
- No need for passwords and the problems of password resets, but just seamless one click sign up and log in - using magic links and Passkeys. -
- And you control your brand of course. -

-
-
-

User Management and Multi-tenancy

-

- Onboard and easily manage your users using permissions and roles and let them create organizations for your - multi-tenant SaaS product. -

-
-
-

Dark Side of the Force

-

- Besides being Enterprise grade, with a security score of 100% and ISO compliancy, everything is not always as - bright as it seems. -

-
- Just click the - - button to switch to the Dark Side. -
-
- - ); -} diff --git a/application/account-management/WebApp/routes/(index)/-components/FeatureSection4.tsx b/application/account-management/WebApp/routes/(index)/-components/FeatureSection4.tsx deleted file mode 100644 index d036fc6790..0000000000 --- a/application/account-management/WebApp/routes/(index)/-components/FeatureSection4.tsx +++ /dev/null @@ -1,60 +0,0 @@ -// FeatureSection4: A functional component that displays a section with features -export function FeatureSection4() { - return ( -
-
-

FEATURES

- Built by Founders, Engineers and Designers -
- Skip the many months it takes to build an enterprise grade and production ready setup. We’ve done the hard - work for you so you can focus on your core product. -
-
-
-
- {/* FeatureBlock components are used to display individual features */} - - - - - - -
-
-
- ); -} - -// FeatureBlock: A functional component that displays a single feature -function FeatureBlock({ title, content }: Readonly) { - return ( -
-
{title}
-
{content}
-
- ); -} - -// FeatureBlockProps: Type definition for the props of FeatureBlock component -interface FeatureBlockProps { - title: string; - content: string; -} diff --git a/application/account-management/WebApp/routes/(index)/-components/FooterSection.tsx b/application/account-management/WebApp/routes/(index)/-components/FooterSection.tsx deleted file mode 100644 index b41ddf5f12..0000000000 --- a/application/account-management/WebApp/routes/(index)/-components/FooterSection.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { Button } from "@repo/ui/components/Button"; -import { TextField } from "@repo/ui/components/TextField"; -import { githubLogo, linkedinLogo, logoWrap, slackLogo, twitterLogo, youtubeLogo } from "./cdnImages"; - -// FooterSection: A functional component that displays the footer section -export function FooterSection() { - return ( - <> -
-
-
Join our newsletter
-
Technology that has your back.
-
-
- - {/* Button component is used to display a call to action */} - -
-
-
-
-
- Logo Wrap -
- Build amazing products on enterprise grade and open source technology. -
-
-
-
-
-
- © 2024 PlatformPlatform. All rights reserved. -
-
- Twitter Logo - LinkedIn Logo - YouTube Logo - GitHub Logo - Slack Logo -
-
-
- - ); -} diff --git a/application/account-management/WebApp/routes/(index)/-components/HeroSection.tsx b/application/account-management/WebApp/routes/(index)/-components/HeroSection.tsx deleted file mode 100644 index 51ce1d9849..0000000000 --- a/application/account-management/WebApp/routes/(index)/-components/HeroSection.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import { LoginButton } from "@repo/infrastructure/auth/LoginButton"; -import { SignUpButton } from "@repo/infrastructure/auth/SignUpButton"; -import { Badge } from "@repo/ui/components/Badge"; -import { Button } from "@repo/ui/components/Button"; -import { Dialog } from "@repo/ui/components/Dialog"; -import { Link } from "@repo/ui/components/Link"; -import { Popover } from "@repo/ui/components/Popover"; -import { ArrowRightIcon, ChevronDownIcon, GithubIcon } from "lucide-react"; -import type React from "react"; -import { DialogTrigger } from "react-aria-components"; -import ThemeModeSelector from "@/federated-modules/common/ThemeModeSelector"; -import logoMark from "@/shared/images/logo-mark.svg"; -import { heroDesktopUrl, heroMobileUrl, logoWrap } from "./cdnImages"; - -// HeroSection: A functional component that displays the hero section -export function HeroSection() { - return ( -
-
-
- logo - logo -
-
- - - -
-
- - - Github - - - Log in - Get started today -
-
-
- - - - - -
-
- Footer - Footer -
-
- ); -} - -// ProductLink: A functional component that displays the product link with dropdown -function ProductLink() { - return ( - - Product - Product - Product - Product - - ); -} - -// ResourcesLink: A functional component that displays the resources link with dropdown -function ResourcesLink() { - return ( - - Product - Product - Product - Product - - ); -} - -// DocumentationLink: A functional component that displays the documentation link with dropdown -function DocumentationLink() { - return ( - - Product - Product - Product - Product - - ); -} - -type TopLinkProps = { - title: string; - children: React.ReactNode; -}; - -function TopLink({ title, children }: Readonly) { - return ( - - - - {children} - - - ); -} - -function FeatureTag() { - return ( - - ); -} - -function ProductTitle() { - return ( -

- Launch your product in minutes - on the best Open Source platform -

- ); -} - -function ProductSubtitle() { - return

Super. Simple. SaaS.

; -} - -function ProductDescription() { - return ( -

- Free, Open-Source .NET, React and Infrastructure kit for Startup and Enterprise. -

- ); -} - -function ActionButtons() { - return ( -
- - Get started today -
- ); -} diff --git a/application/account-management/WebApp/routes/(index)/-components/TechnologySection.tsx b/application/account-management/WebApp/routes/(index)/-components/TechnologySection.tsx deleted file mode 100644 index a9effe8f17..0000000000 --- a/application/account-management/WebApp/routes/(index)/-components/TechnologySection.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { frame1, frame2, frame3, frame4, frame5, frame6 } from "./cdnImages"; - -// TechnologySection: A functional component that displays the technology section -export function TechnologySection() { - return ( -
-
-

FEATURES

-

- Standing on the shoulders of giants. Building blocks of PlatformPlatform -

-
-
-
- {/* FeatureBlock component is used to display individual feature blocks */} - - - - - - -
-
-
- ); -} - -interface FeatureBlockProps { - title: string; - content: string; - image: string; -} - -// FeatureBlock: A functional component that displays individual feature blocks -function FeatureBlock({ title, content, image }: Readonly) { - return ( -
-
-
- {title} -
-
-
{title}
-
{content}
-
- ); -} diff --git a/application/account-management/WebApp/routes/(index)/-components/TechnologySection2.tsx b/application/account-management/WebApp/routes/(index)/-components/TechnologySection2.tsx deleted file mode 100644 index 09960aefcc..0000000000 --- a/application/account-management/WebApp/routes/(index)/-components/TechnologySection2.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { infrastructure } from "./cdnImages"; - -// TechnologySection2: A functional component that displays the technology section -export function TechnologySection2() { - return ( -
-
-
- {/* Display the section title */} - -
-
-
-
- {/* Display the feature list */} - -
-
-
- Mockup -
-
-
-
- ); -} - -// FeatureTitle: A functional component that displays the section title -function FeatureTitle() { - return ( -
-

TECHNOLOGY

-

- We’ve taken care of all the tech stuff, so you can focus on getting your product launched -

-

- Drawing on our expertise building true enterprise-grade products with millions of daily users in highly - regulated sectors like healthcare, finance, government, etc., we help you create secure production-ready - products. -

-
- ); -} - -// FeatureList: A functional component that displays the feature list -function FeatureList() { - return ( -
-
-

Monorepo containing all application code

-

- PlatformPlatform is a monorepo containing all application code, infrastructure, tools, libraries, - documentation, etc. A monorepo is a powerful way to organize a codebase, used by Google, Facebook, Uber, - Microsoft, etc. -

-
-
-

Deploy Azure Infrastructure

-

- This is how it looks when GitHub workflows has deployed Azure Infrastructure: -

-
-
-

100% Security score

-

- This is the security score after deploying PlatformPlatform resources to Azure. Achieving a 100% security - score in Azure Defender for Cloud without exemptions is not trivial. -

-
-
- ); -} diff --git a/application/account-management/WebApp/routes/(index)/-components/cdnImages.ts b/application/account-management/WebApp/routes/(index)/-components/cdnImages.ts deleted file mode 100644 index 4a0055e507..0000000000 --- a/application/account-management/WebApp/routes/(index)/-components/cdnImages.ts +++ /dev/null @@ -1,28 +0,0 @@ -export const heroDesktopUrl = "https://platformplatformgithub.blob.core.windows.net/hero-image-desktop.png"; -export const heroMobileUrl = "https://platformplatformgithub.blob.core.windows.net/hero-image-mobile.webp"; -export const logoWrap = "https://platformplatformgithub.blob.core.windows.net/logo-wrap.svg"; -export const twitterLogo = "https://platformplatformgithub.blob.core.windows.net/twitter-x.svg"; -export const linkedinLogo = "https://platformplatformgithub.blob.core.windows.net/linkedin.svg"; -export const youtubeLogo = "https://platformplatformgithub.blob.core.windows.net/youtube-logo.svg"; -export const githubLogo = "https://platformplatformgithub.blob.core.windows.net/github2.svg"; -export const slackLogo = "https://platformplatformgithub.blob.core.windows.net/slack2.svg"; -export const youtubeImageUrl = "https://platformplatformgithub.blob.core.windows.net/youtube.svg"; -export const slackImageUrl = "https://platformplatformgithub.blob.core.windows.net/slack.svg"; -export const githubImageUrl = "https://platformplatformgithub.blob.core.windows.net/github.svg"; -export const createAccountUrl = "https://platformplatformgithub.blob.core.windows.net/create-account.png"; -export const enterCodeUrl = "https://platformplatformgithub.blob.core.windows.net/enter-code.png"; -export const profileEditDarkUrl = "https://platformplatformgithub.blob.core.windows.net/profile-edit-dark.png"; -export const profileEditLightUrl = "https://platformplatformgithub.blob.core.windows.net/profile-edit.png"; -export const editProfileUrl = "https://platformplatformgithub.blob.core.windows.net/edit-profile.png"; -export const accountSettingsUrl = "https://platformplatformgithub.blob.core.windows.net/account-setting.png"; -export const usersUrl = "https://platformplatformgithub.blob.core.windows.net/users.png"; -export const darkModeUrl = "https://platformplatformgithub.blob.core.windows.net/dark-mode.png"; -export const lightModeUrl = "https://platformplatformgithub.blob.core.windows.net/light-mode.png"; -export const frame1 = "https://platformplatformgithub.blob.core.windows.net/dotnet.svg"; -export const frame2 = "https://platformplatformgithub.blob.core.windows.net/react.svg"; -export const frame3 = "https://platformplatformgithub.blob.core.windows.net/github.svg"; -export const frame4 = "https://platformplatformgithub.blob.core.windows.net/azure.svg"; -export const frame5 = "https://platformplatformgithub.blob.core.windows.net/help-square.svg"; -export const frame6 = "https://platformplatformgithub.blob.core.windows.net/shield-star.svg"; -export const infrastructure = "https://platformplatformgithub.blob.core.windows.net/infrastructure.png"; -export const calenderMockupUrl = "https://platformplatformgithub.blob.core.windows.net/mockup-calender.png"; diff --git a/application/account-management/WebApp/routes/(index)/index.tsx b/application/account-management/WebApp/routes/(index)/index.tsx index 94c7b5d598..a17529ed3a 100644 --- a/application/account-management/WebApp/routes/(index)/index.tsx +++ b/application/account-management/WebApp/routes/(index)/index.tsx @@ -1,41 +1,37 @@ -import { loggedInPath } from "@repo/infrastructure/auth/constants"; -import { useIsAuthenticated } from "@repo/infrastructure/auth/hooks"; -import { createFileRoute, Navigate } from "@tanstack/react-router"; -import { CommunitySection } from "./-components/CommunitySection"; -import { CtaSection } from "./-components/CtaSection"; -import { CtaSection2 } from "./-components/CtaSection2"; -import { CtaSection3 } from "./-components/CtaSection3"; -import { FeatureSection } from "./-components/FeatureSection"; -import { FeatureSection2 } from "./-components/FeatureSection2"; -import { FeatureSection3 } from "./-components/FeatureSection3"; -import { FeatureSection4 } from "./-components/FeatureSection4"; -import { FooterSection } from "./-components/FooterSection"; -import { HeroSection } from "./-components/HeroSection"; -import { TechnologySection } from "./-components/TechnologySection"; -import { TechnologySection2 } from "./-components/TechnologySection2"; +import { Trans } from "@lingui/react/macro"; +import { createFileRoute } from "@tanstack/react-router"; +import { PublicFooter } from "@/shared/components/PublicFooter"; +import { PublicNavigation } from "@/shared/components/PublicNavigation"; export const Route = createFileRoute("/(index)/")({ + beforeLoad: () => ({ disableAuthSync: true }), component: function LandingPage() { - const isAuthenticated = useIsAuthenticated(); + return ( +
+ {/* Hero Section */} +
+ - if (isAuthenticated) { - return ; - } + {/* Hero Content */} +
+
+ {/* Title */} +
+

+ Welcome to PlatformPlatform +

+

+ You successfully installed PlatformPlatform! 🎉 +

+

+ Replace this sample page with your own product information and branding. +

+
+
+
+
- return ( -
- - - - - - - - - - - - +
); } diff --git a/application/account-management/WebApp/shared/components/PublicFooter.tsx b/application/account-management/WebApp/shared/components/PublicFooter.tsx new file mode 100644 index 0000000000..d77132acd3 --- /dev/null +++ b/application/account-management/WebApp/shared/components/PublicFooter.tsx @@ -0,0 +1,113 @@ +import { Trans } from "@lingui/react/macro"; +import { Image } from "@repo/ui/components/Image"; +import { Link } from "@repo/ui/components/Link"; +import { Tooltip, TooltipTrigger } from "@repo/ui/components/Tooltip"; +import { GithubIcon, LinkedinIcon, MailIcon, YoutubeIcon } from "lucide-react"; +import logoMark from "@/shared/images/logo-mark.svg"; +import logoWrap from "@/shared/images/logo-wrap.svg"; + +export function PublicFooter() { + const currentYear = new Date().getFullYear(); + + return ( +
+
+ {/* Main Footer Content - Desktop: side by side, Mobile: stacked */} +
+ {/* Logo - Full logo on mobile, mark on desktop */} +
+ PlatformPlatform + PlatformPlatform +
+ + {/* Content */} +
+ {/* Description */} +
+

+ PlatformPlatform +

+

+ Free, open-source .NET and React starter kit for building modern SaaS applications. +

+
+
+
+ + {/* Bottom Section */} +
+ {/* Copyright */} +
+ © {currentYear} PlatformPlatform. All rights reserved. +
+ + {/* Social Links */} +
+ + + + + + Email + + + + + + + + LinkedIn + + + + + + + + YouTube + + + + + + + + GitHub + + +
+
+
+
+ ); +} diff --git a/application/account-management/WebApp/shared/components/PublicNavigation.tsx b/application/account-management/WebApp/shared/components/PublicNavigation.tsx new file mode 100644 index 0000000000..16bcbeff3f --- /dev/null +++ b/application/account-management/WebApp/shared/components/PublicNavigation.tsx @@ -0,0 +1,77 @@ +import { t } from "@lingui/core/macro"; +import { Trans } from "@lingui/react/macro"; +import { loginPath, signUpPath } from "@repo/infrastructure/auth/constants"; +import { useIsAuthenticated } from "@repo/infrastructure/auth/hooks"; +import { Image } from "@repo/ui/components/Image"; +import { Link } from "@repo/ui/components/Link"; +import LocaleSwitcher from "@/federated-modules/common/LocaleSwitcher"; +import SupportButton from "@/federated-modules/common/SupportButton"; +import ThemeModeSelector from "@/federated-modules/common/ThemeModeSelector"; +import AvatarButton from "@/federated-modules/topMenu/AvatarButton"; +import logoMark from "@/shared/images/logo-mark.svg"; +import logoWrap from "@/shared/images/logo-wrap.svg"; + +export function PublicNavigation() { + const isAuthenticated = useIsAuthenticated(); + + return ( + + ); +} diff --git a/application/account-management/WebApp/shared/translations/locale/da-DK.po b/application/account-management/WebApp/shared/translations/locale/da-DK.po index 22a5e0fa6c..7fc0cbf716 100644 --- a/application/account-management/WebApp/shared/translations/locale/da-DK.po +++ b/application/account-management/WebApp/shared/translations/locale/da-DK.po @@ -17,6 +17,9 @@ msgstr "" msgid "{0} users deleted successfully" msgstr "{0} brugere slettet succesfuldt" +msgid "© {currentYear} PlatformPlatform. All rights reserved." +msgstr "© {currentYear} PlatformPlatform. Alle rettigheder forbeholdes." + msgid "A different user logged in from another browser tab." msgstr "En anden bruger loggede ind fra en anden browsertab." @@ -255,6 +258,12 @@ msgstr "Filtre" msgid "First name" msgstr "Fornavn" +msgid "Free, open-source .NET and React starter kit for building modern SaaS applications." +msgstr "Gratis, open-source .NET og React startpakke til at bygge moderne SaaS-applikationer." + +msgid "GitHub" +msgstr "GitHub" + msgid "Go to account settings" msgstr "Gå til kontoindstillinger" @@ -291,6 +300,9 @@ msgstr "Efternavn" msgid "Light" msgstr "Lys" +msgid "LinkedIn" +msgstr "LinkedIn" + msgid "Log in" msgstr "Log ind" @@ -402,6 +414,9 @@ msgstr "Fjern logo" msgid "Remove profile picture" msgstr "Fjern profilbillede" +msgid "Replace this sample page with your own product information and branding." +msgstr "Erstat denne eksempelsside med dine egne produktoplysninger og branding." + msgid "Request a new code" msgstr "Anmod om en ny kode" @@ -442,6 +457,9 @@ msgstr "Indstillinger" msgid "Show filters" msgstr "Vis filtre" +msgid "Sign up" +msgstr "Tilmeld dig" + msgid "Sign up in seconds to start building on PlatformPlatform – just like thousands of others." msgstr "Tilmeld dig på sekunder for at bygge på PlatformPlatform – ligesom tusinder andre." @@ -569,6 +587,9 @@ msgstr "Velkommen hjem" msgid "Welcome home, {0}" msgstr "Velkommen hjem, {0}" +msgid "Welcome to PlatformPlatform" +msgstr "Velkommen til PlatformPlatform" + msgid "When you accept this invitation, your profile information (name, title, and avatar) from your current account will be copied to the new account." msgstr "Når du accepterer denne invitation, vil dine profiloplysninger (navn, titel og avatar) fra din nuværende konto blive kopieret til den nye konto." @@ -576,6 +597,9 @@ msgstr "Når du accepterer denne invitation, vil dine profiloplysninger (navn, t msgid "You have been invited to join <0>{0}." msgstr "Du er blevet inviteret til at deltage i <0>{0}." +msgid "You successfully installed PlatformPlatform! 🎉" +msgstr "Du har succesfuldt installeret PlatformPlatform! 🎉" + msgid "You were logged out from another browser tab." msgstr "Du blev logget ud fra en anden browsertab." @@ -599,3 +623,6 @@ msgstr "Din bekræftelseskode er gyldig i {expiresInString}" msgid "yourname@example.com" msgstr "ditnavn@eksempel.com" + +msgid "YouTube" +msgstr "YouTube" diff --git a/application/account-management/WebApp/shared/translations/locale/en-US.po b/application/account-management/WebApp/shared/translations/locale/en-US.po index ce640c089f..95df63c658 100644 --- a/application/account-management/WebApp/shared/translations/locale/en-US.po +++ b/application/account-management/WebApp/shared/translations/locale/en-US.po @@ -17,6 +17,9 @@ msgstr "" msgid "{0} users deleted successfully" msgstr "{0} users deleted successfully" +msgid "© {currentYear} PlatformPlatform. All rights reserved." +msgstr "© {currentYear} PlatformPlatform. All rights reserved." + msgid "A different user logged in from another browser tab." msgstr "A different user logged in from another browser tab." @@ -255,6 +258,12 @@ msgstr "Filters" msgid "First name" msgstr "First name" +msgid "Free, open-source .NET and React starter kit for building modern SaaS applications." +msgstr "Free, open-source .NET and React starter kit for building modern SaaS applications." + +msgid "GitHub" +msgstr "GitHub" + msgid "Go to account settings" msgstr "Go to account settings" @@ -291,6 +300,9 @@ msgstr "Last name" msgid "Light" msgstr "Light" +msgid "LinkedIn" +msgstr "LinkedIn" + msgid "Log in" msgstr "Log in" @@ -402,6 +414,9 @@ msgstr "Remove logo" msgid "Remove profile picture" msgstr "Remove profile picture" +msgid "Replace this sample page with your own product information and branding." +msgstr "Replace this sample page with your own product information and branding." + msgid "Request a new code" msgstr "Request a new code" @@ -442,6 +457,9 @@ msgstr "Settings" msgid "Show filters" msgstr "Show filters" +msgid "Sign up" +msgstr "Sign up" + msgid "Sign up in seconds to start building on PlatformPlatform – just like thousands of others." msgstr "Sign up in seconds to start building on PlatformPlatform – just like thousands of others." @@ -569,6 +587,9 @@ msgstr "Welcome home" msgid "Welcome home, {0}" msgstr "Welcome home, {0}" +msgid "Welcome to PlatformPlatform" +msgstr "Welcome to PlatformPlatform" + msgid "When you accept this invitation, your profile information (name, title, and avatar) from your current account will be copied to the new account." msgstr "When you accept this invitation, your profile information (name, title, and avatar) from your current account will be copied to the new account." @@ -576,6 +597,9 @@ msgstr "When you accept this invitation, your profile information (name, title, msgid "You have been invited to join <0>{0}." msgstr "You have been invited to join <0>{0}." +msgid "You successfully installed PlatformPlatform! 🎉" +msgstr "You successfully installed PlatformPlatform! 🎉" + msgid "You were logged out from another browser tab." msgstr "You were logged out from another browser tab." @@ -599,3 +623,6 @@ msgstr "Your verification code is valid for {expiresInString}" msgid "yourname@example.com" msgstr "yourname@example.com" + +msgid "YouTube" +msgstr "YouTube" diff --git a/application/shared-kernel/SharedKernel/SinglePageApp/SinglePageAppConfiguration.cs b/application/shared-kernel/SharedKernel/SinglePageApp/SinglePageAppConfiguration.cs index c8619302b7..8e80e39250 100644 --- a/application/shared-kernel/SharedKernel/SinglePageApp/SinglePageAppConfiguration.cs +++ b/application/shared-kernel/SharedKernel/SinglePageApp/SinglePageAppConfiguration.cs @@ -141,8 +141,7 @@ private static StringValues GetPermissionsPolicies() private string GetContentSecurityPolicies() { - const string trustedCdnHost = "https://platformplatformgithub.blob.core.windows.net"; - var trustedHosts = $"{PublicUrl} {CdnUrl} {trustedCdnHost}"; + var trustedHosts = $"{PublicUrl} {CdnUrl}"; if (_isDevelopment) { From e5230d1738e157953533816100607c9c14b724be Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Mon, 17 Nov 2025 18:01:48 +0100 Subject: [PATCH 3/5] Fix tenant switching to preserve current path after landing page redesign --- .../WebApp/federated-modules/common/TenantSelector.tsx | 8 ++++---- .../federated-modules/sideMenu/FederatedSideMenu.tsx | 8 ++++---- .../WebApp/tests/e2e/tenant-switching-flows.spec.ts | 10 +++++----- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/application/account-management/WebApp/federated-modules/common/TenantSelector.tsx b/application/account-management/WebApp/federated-modules/common/TenantSelector.tsx index d27fdf180d..64ac2fb8a9 100644 --- a/application/account-management/WebApp/federated-modules/common/TenantSelector.tsx +++ b/application/account-management/WebApp/federated-modules/common/TenantSelector.tsx @@ -1,6 +1,7 @@ import { t } from "@lingui/core/macro"; import { Trans } from "@lingui/react/macro"; import type { UserInfo } from "@repo/infrastructure/auth/AuthenticationProvider"; +import { loggedInPath } from "@repo/infrastructure/auth/constants"; import { useUserInfo } from "@repo/infrastructure/auth/hooks"; import { Badge } from "@repo/ui/components/Badge"; import { Button } from "@repo/ui/components/Button"; @@ -270,11 +271,10 @@ export default function TenantSelector({ onShowInvitationDialog, variant = "defa setIsSwitching(true); }, onSuccess: () => { - // Keep the loader visible briefly before redirecting + // Stay on the same path, but default to loggedInPath if on root // Don't set isSwitching to false here - let the redirect handle it - setTimeout(() => { - window.location.href = "/"; - }, 250); + const targetPath = window.location.pathname === "/" ? loggedInPath : window.location.pathname; + window.location.href = targetPath; }, onError: () => { // Hide the loader on error diff --git a/application/account-management/WebApp/federated-modules/sideMenu/FederatedSideMenu.tsx b/application/account-management/WebApp/federated-modules/sideMenu/FederatedSideMenu.tsx index 8e39d60472..93fd353714 100644 --- a/application/account-management/WebApp/federated-modules/sideMenu/FederatedSideMenu.tsx +++ b/application/account-management/WebApp/federated-modules/sideMenu/FederatedSideMenu.tsx @@ -1,4 +1,5 @@ import { t } from "@lingui/core/macro"; +import { loggedInPath } from "@repo/infrastructure/auth/constants"; import { useUserInfo } from "@repo/infrastructure/auth/hooks"; import { SideMenu } from "@repo/ui/components/SideMenu"; import { useState } from "react"; @@ -30,10 +31,9 @@ export default function FederatedSideMenu({ currentSystem }: Readonly { - // Keep the loader visible briefly before redirecting - setTimeout(() => { - window.location.href = "/"; - }, 250); + // Stay on the same path, but default to loggedInPath if on root + const targetPath = globalThis.location.pathname === "/" ? loggedInPath : globalThis.location.pathname; + globalThis.location.href = targetPath; }, onError: () => { setIsSwitching(false); diff --git a/application/account-management/WebApp/tests/e2e/tenant-switching-flows.spec.ts b/application/account-management/WebApp/tests/e2e/tenant-switching-flows.spec.ts index 442ac005ac..0d5c2bfc74 100644 --- a/application/account-management/WebApp/tests/e2e/tenant-switching-flows.spec.ts +++ b/application/account-management/WebApp/tests/e2e/tenant-switching-flows.spec.ts @@ -179,7 +179,7 @@ test.describe("@comprehensive", () => { await page1.getByRole("button", { name: "Accept invitation" }).click(); await expect(page1.locator('nav[aria-label="Main navigation"]')).toBeVisible(); - await expect(page1).toHaveURL("/admin"); + await expect(page1).toHaveURL("/admin/users"); // Re-query the tenant button after page changes const navElementAfter = page1.locator("nav").first(); @@ -208,7 +208,7 @@ test.describe("@comprehensive", () => { await page1.getByRole("button", { name: "Accept invitation" }).click(); await expect(page1.locator('nav[aria-label="Main navigation"]')).toBeVisible(); - await expect(page1).toHaveURL("/admin"); + await expect(page1).toHaveURL("/admin/users"); // Re-query tenant button after page changes const navElementAfter = page1.locator("nav").first(); @@ -246,7 +246,7 @@ test.describe("@comprehensive", () => { await page1.getByRole("button", { name: "User profile menu" }).click(); await page1.getByRole("menuitem", { name: "Log out" }).click(); await expect(page1.getByRole("heading", { name: "Hi! Welcome back" })).toBeVisible(); - await expect(page1).toHaveURL("login?returnPath=%2Fadmin"); + await expect(page1).toHaveURL("login?returnPath=%2Fadmin%2Fusers"); // tab 2 also loses authentication await page2.goto("/admin"); @@ -261,7 +261,7 @@ test.describe("@comprehensive", () => { // Wait for navigation to complete await expect(page1.locator('nav[aria-label="Main navigation"]')).toBeVisible(); - await expect(page1).toHaveURL("/admin"); + await expect(page1).toHaveURL("/admin/users"); // Should login to tertiary tenant (last selected) const navElementAfter = page1.locator("nav").first(); @@ -305,7 +305,7 @@ test.describe("@comprehensive", () => { const targetMenuItem = menuItems.filter({ hasText: primaryTenantName }).first(); await targetMenuItem.click(); - await expect(page1).toHaveURL("/admin"); + await expect(page1).toHaveURL("/admin/account"); // Re-query the tenant button with the new tenant name const navElementAfterSwitch = page1.locator("nav").first(); From 54fc9b8e73772414d4a20e9b0511668dfe79da32 Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Sun, 16 Nov 2025 19:27:22 +0100 Subject: [PATCH 4/5] Change login/signup layout breakpoint from md to lg --- .../WebApp/shared/layouts/HorizontalHeroLayout.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/account-management/WebApp/shared/layouts/HorizontalHeroLayout.tsx b/application/account-management/WebApp/shared/layouts/HorizontalHeroLayout.tsx index 36e65db917..571057440f 100644 --- a/application/account-management/WebApp/shared/layouts/HorizontalHeroLayout.tsx +++ b/application/account-management/WebApp/shared/layouts/HorizontalHeroLayout.tsx @@ -17,8 +17,8 @@ export function HorizontalHeroLayout({ children }: Readonly -
-
+
+
{children} {/* Mobile-only icon controls at bottom of form */}
@@ -27,7 +27,7 @@ export function HorizontalHeroLayout({ children }: Readonly
-
+
From 228aeceeff52bd81e7d1e39c27d11bb495aa1335 Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Sun, 16 Nov 2025 19:40:25 +0100 Subject: [PATCH 5/5] Update attribution to 'Built with' and add HTML comment --- .../account-management/WebApp/routes/login/index.tsx | 6 +++++- .../account-management/WebApp/routes/signup/index.tsx | 6 +++++- .../WebApp/shared/translations/locale/da-DK.po | 3 +++ .../WebApp/shared/translations/locale/en-US.po | 3 +++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/application/account-management/WebApp/routes/login/index.tsx b/application/account-management/WebApp/routes/login/index.tsx index ec6d17cd77..63f5f23bc0 100644 --- a/application/account-management/WebApp/routes/login/index.tsx +++ b/application/account-management/WebApp/routes/login/index.tsx @@ -104,9 +104,13 @@ export function LoginForm() { Don't have an account? Create one

+ {/* + Built with PlatformPlatform - https://github.com/platformplatform/PlatformPlatform + We'd appreciate it if you keep this attribution to help others discover this free, open-source platform. Thank you! 🙏 + */}
- Powered by + Built with {t`PlatformPlatform`} diff --git a/application/account-management/WebApp/routes/signup/index.tsx b/application/account-management/WebApp/routes/signup/index.tsx index b8a710b6bd..a17e744254 100644 --- a/application/account-management/WebApp/routes/signup/index.tsx +++ b/application/account-management/WebApp/routes/signup/index.tsx @@ -127,9 +127,13 @@ export function StartSignupForm() {
+ {/* + Built with PlatformPlatform - https://github.com/platformplatform/PlatformPlatform + We'd appreciate it if you keep this attribution to help others discover this free, open-source platform. Thank you! 🙏 + */}
- Powered by + Built with {t`PlatformPlatform`} diff --git a/application/account-management/WebApp/shared/translations/locale/da-DK.po b/application/account-management/WebApp/shared/translations/locale/da-DK.po index 7fc0cbf716..8fba67ad3f 100644 --- a/application/account-management/WebApp/shared/translations/locale/da-DK.po +++ b/application/account-management/WebApp/shared/translations/locale/da-DK.po @@ -112,6 +112,9 @@ msgstr "Tilbage til login" msgid "Back to signup" msgstr "Tilbage til tilmelding" +msgid "Built with" +msgstr "Bygget med" + msgid "By continuing, you accept our policies" msgstr "Ved at fortsætte accepterer du vores vilkår" diff --git a/application/account-management/WebApp/shared/translations/locale/en-US.po b/application/account-management/WebApp/shared/translations/locale/en-US.po index 95df63c658..fa74fd7b91 100644 --- a/application/account-management/WebApp/shared/translations/locale/en-US.po +++ b/application/account-management/WebApp/shared/translations/locale/en-US.po @@ -112,6 +112,9 @@ msgstr "Back to login" msgid "Back to signup" msgstr "Back to signup" +msgid "Built with" +msgstr "Built with" + msgid "By continuing, you accept our policies" msgstr "By continuing, you accept our policies"