From ed4c59c464f7b4ae366f06e7908073b5a32a6249 Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Fri, 22 Nov 2024 00:44:22 +0100 Subject: [PATCH 1/5] Update README with .NET 9, C# 13, React 19, and latest project structure --- README.md | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index b0d306c96..593d2e865 100644 --- a/README.md +++ b/README.md @@ -23,12 +23,12 @@ # 👋 Welcome to PlatformPlatform -Kick-start building top-tier B2B & B2C cloud SaaS products with sleek design, fully localized and accessible, vertical slice architecture, automated and fast DevOps, and top-notch security. All in one place – at zero cost. +Kick-start building top-tier B2B & B2C cloud SaaS products with sleek design, fully localized and accessible, vertical slice architecture, automated and fast DevOps, and top-notch security. This is in the box: -* **Backend** - .NET and C# adhering to the principles of vertical slice architecture, DDD, CQRS, and clean code -* **Frontend** - React using TypeScript, with a sleek fully localized UI and a mature accessible design system +* **Backend** - .NET 9 and C# adhering to the principles of vertical slice architecture, DDD, CQRS, and clean code +* **Frontend** - React 19 and TypeScript, fully localized using React Aria components for world-class accessibility * **CI/CD** - GitHub actions for fast passwordless deployments of application (Docker) and infrastructure (Bicep) * **Infrastructure** - Cost efficient and scalable Azure PaaS services like Azure Container Apps, Azure SQL, etc. * **Developer CLI** - Extendable .NET CLI for DevEx - set up CI/CD is one command and a couple of questions @@ -61,7 +61,7 @@ For development, you need .NET, Docker, and Node. And GitHub and Azure CLI for s ```powershell @( - "Microsoft.DotNet.SDK.8", + "Microsoft.DotNet.SDK.9", "Git.Git", "Docker.DockerDesktop", "OpenJS.NodeJS", @@ -80,7 +80,8 @@ Open a terminal and run the following commands: - Install [Homebrew](https://brew.sh/), a package manager for Mac - `brew install --cask dotnet-sdk` -- `brew install git docker node azure-cli gh` +- `brew install --cask docker` +- `brew install git node azure-cli gh` @@ -127,10 +128,10 @@ Open a terminal and run the following commands: sudo apt-get update ``` -- Install .NET SDK 8.0, Node, GitHub CLI +- Install .NET SDK 9.0, Node, GitHub CLI ```bash - sudo apt-get install -y dotnet-sdk-8.0 nodejs gh + sudo apt-get install -y dotnet-sdk-9.0 nodejs gh ``` - Install Azure CLI @@ -157,7 +158,7 @@ Open a terminal and run the following commands: -## 1. Fork and clone the repository +## 1. Clone the repository Forking is only required to configure GitHub repository with continuous deployments to Azure ([step 3](#4-set-up-cicd-with-passwordless-deployments-from-github-to-azure)). @@ -211,7 +212,8 @@ PlatformPlatform is a [monorepo](https://en.wikipedia.org/wiki/Monorepo) contain │ │ ├─ Core # Core business logic, application use cases, and infrastructure │ │ ├─ Workers # Background workers for long-running tasks and event processing │ │ └─ Tests # Tests for the Api, Core, and Workers -│ ├─ shared-kernel # Reusable components for all self-contained systems +│ ├─ shared-kernel # Reusable components and default configuration for all systems +│ ├─ shared-webapp # Reusable and styled React Aria Components that affect all systems │ ├─ [saas-scs] # [Your SCS] Create your SaaS product as a self-contained system │ └─ back-office # A self-contained system for operations and support (empty for now) ├─ cloud-infrastructure # Contains Bash and Bicep scripts (IaC) for Azure resources @@ -225,11 +227,11 @@ PlatformPlatform is a [monorepo](https://en.wikipedia.org/wiki/Monorepo) contain # Technologies -### .NET 8 Backend With Vertical Sliced Architecture, DDD, CQRS, Minimal API, and Aspire +### .NET 9 Backend With Vertical Sliced Architecture, DDD, CQRS, Minimal API, and Aspire The backend is built using the most popular, mature, and commonly used technologies in the .NET ecosystem: -- [.NET 8](https://dotnet.microsoft.com) and [C# 12](https://learn.microsoft.com/en-us/dotnet/csharp/tour-of-csharp) +- [.NET 9](https://dotnet.microsoft.com) and [C# 13](https://learn.microsoft.com/en-us/dotnet/csharp/tour-of-csharp) - [.NET Aspire](https://aka.ms/dotnet-aspire) - [YARP](https://microsoft.github.io/reverse-proxy) - [ASP.NET Minimal API](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis) @@ -258,11 +260,11 @@ Although some features like multi-tenancy are not yet implemented, the current i -### React Frontend With TypeScript, React Aria Components, and Node +### React 19 Frontend With TypeScript, React Aria Components, and Node The frontend is built with these technologies: -- [React](https://react.dev) +- [React 19](https://react.dev) - [TypeScript](https://www.typescriptlang.org) - [React Aria Components](https://react-spectrum.adobe.com/react-aria/react-aria-components.html) - [Tanstack Router](https://tanstack.com) From bdb7e7533ca3ef9b2a2ae2560a16941eb128d978 Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Sat, 23 Nov 2024 13:05:41 +0100 Subject: [PATCH 2/5] Update Issuer and Audience settings for Localhost --- .../TokenSigning/DevelopmentTokenSigningClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/shared-kernel/SharedKernel/Authentication/TokenSigning/DevelopmentTokenSigningClient.cs b/application/shared-kernel/SharedKernel/Authentication/TokenSigning/DevelopmentTokenSigningClient.cs index 17ef588c8..676e18588 100644 --- a/application/shared-kernel/SharedKernel/Authentication/TokenSigning/DevelopmentTokenSigningClient.cs +++ b/application/shared-kernel/SharedKernel/Authentication/TokenSigning/DevelopmentTokenSigningClient.cs @@ -13,9 +13,9 @@ public class DevelopmentTokenSigningClient private static string UserSecretsId => EntryAssembly.GetCustomAttribute()!.UserSecretsId; - public string Issuer => "https://localhost:9000"; + public string Issuer => "Localhost"; - public string Audience => "https://localhost:9000"; + public string Audience => "Localhost"; public SigningCredentials GetSigningCredentials() { From 78ccb02884e172354fd83120e2040e42b4644615 Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Thu, 28 Nov 2024 07:51:49 +0100 Subject: [PATCH 3/5] Change GET endpoints to use [AsParameters] for automatic query binding instead of manual instantiation --- .../account-management/Api/Endpoints/TenantEndpoints.cs | 4 ++-- application/account-management/Api/Endpoints/UserEndpoints.cs | 4 ++-- .../account-management/Tests/Tenants/GetTenantTests.cs | 2 +- application/account-management/Tests/Users/GetUserTests.cs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/application/account-management/Api/Endpoints/TenantEndpoints.cs b/application/account-management/Api/Endpoints/TenantEndpoints.cs index f36d1bd87..244ae94f5 100644 --- a/application/account-management/Api/Endpoints/TenantEndpoints.cs +++ b/application/account-management/Api/Endpoints/TenantEndpoints.cs @@ -14,8 +14,8 @@ public void MapEndpoints(IEndpointRouteBuilder routes) { var group = routes.MapGroup(RoutesPrefix).WithTags("Tenants").RequireAuthorization(); - group.MapGet("/{id}", async Task> (TenantId id, IMediator mediator) - => await mediator.Send(new GetTenantQuery(id)) + group.MapGet("/{id}", async Task> ([AsParameters] GetTenantQuery query, IMediator mediator) + => await mediator.Send(query) ).Produces(); group.MapPut("/{id}", async Task (TenantId id, UpdateTenantCommand command, IMediator mediator) diff --git a/application/account-management/Api/Endpoints/UserEndpoints.cs b/application/account-management/Api/Endpoints/UserEndpoints.cs index 5e0554986..106aa3fa4 100644 --- a/application/account-management/Api/Endpoints/UserEndpoints.cs +++ b/application/account-management/Api/Endpoints/UserEndpoints.cs @@ -18,8 +18,8 @@ public void MapEndpoints(IEndpointRouteBuilder routes) => await mediator.Send(query) ).Produces(); - group.MapGet("/{id}", async Task> (UserId id, IMediator mediator) - => await mediator.Send(new GetUserQuery(id)) + group.MapGet("/{id}", async Task> ([AsParameters] GetUserQuery query, IMediator mediator) + => await mediator.Send(query) ).Produces(); group.MapPost("/", async Task (CreateUserCommand command, IMediator mediator) diff --git a/application/account-management/Tests/Tenants/GetTenantTests.cs b/application/account-management/Tests/Tenants/GetTenantTests.cs index 526535baa..9b0cac682 100644 --- a/application/account-management/Tests/Tenants/GetTenantTests.cs +++ b/application/account-management/Tests/Tenants/GetTenantTests.cs @@ -65,6 +65,6 @@ public async Task GetTenant_WhenTenantInvalidTenantId_ShouldReturnBadRequest() var response = await AuthenticatedHttpClient.GetAsync($"/api/account-management/tenants/{invalidTenantId}"); // Assert - await response.ShouldHaveErrorStatusCode(HttpStatusCode.BadRequest, $"""Failed to bind parameter "TenantId id" from "{invalidTenantId}"."""); + await response.ShouldHaveErrorStatusCode(HttpStatusCode.BadRequest, $"""Failed to bind parameter "TenantId Id" from "{invalidTenantId}"."""); } } diff --git a/application/account-management/Tests/Users/GetUserTests.cs b/application/account-management/Tests/Users/GetUserTests.cs index 83eb6784e..ae965148e 100644 --- a/application/account-management/Tests/Users/GetUserTests.cs +++ b/application/account-management/Tests/Users/GetUserTests.cs @@ -71,6 +71,6 @@ public async Task GetUser_WhenInvalidUserId_ShouldReturnBadRequest() var response = await AuthenticatedHttpClient.GetAsync($"/api/account-management/users/{invalidUserId}"); // Assert - await response.ShouldHaveErrorStatusCode(HttpStatusCode.BadRequest, $"""Failed to bind parameter "UserId id" from "{invalidUserId}"."""); + await response.ShouldHaveErrorStatusCode(HttpStatusCode.BadRequest, $"""Failed to bind parameter "UserId Id" from "{invalidUserId}"."""); } } From 1620ba7f85fb7f0c87846ffe4dfd85a045c3ed3a Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Mon, 16 Dec 2024 13:12:02 +0100 Subject: [PATCH 4/5] Change useFormState from ReactDOM to useActionState in React 19 --- .../routes/admin/users/-components/InviteUserModal.tsx | 5 ++--- application/account-management/WebApp/routes/login/index.tsx | 5 ++--- .../account-management/WebApp/routes/login/verify.tsx | 5 ++--- .../account-management/WebApp/routes/signup/index.tsx | 5 ++--- .../account-management/WebApp/routes/signup/verify.tsx | 5 ++--- .../WebApp/shared/components/userModals/UserProfileModal.tsx | 5 ++--- 6 files changed, 12 insertions(+), 18 deletions(-) diff --git a/application/account-management/WebApp/routes/admin/users/-components/InviteUserModal.tsx b/application/account-management/WebApp/routes/admin/users/-components/InviteUserModal.tsx index bf6872643..4fff7b9b7 100644 --- a/application/account-management/WebApp/routes/admin/users/-components/InviteUserModal.tsx +++ b/application/account-management/WebApp/routes/admin/users/-components/InviteUserModal.tsx @@ -1,5 +1,4 @@ -import { useCallback, useEffect } from "react"; -import { useFormState } from "react-dom"; +import { useActionState, useCallback, useEffect } from "react"; import { Button } from "@repo/ui/components/Button"; import { TextField } from "@repo/ui/components/TextField"; import { Heading } from "@repo/ui/components/Heading"; @@ -22,7 +21,7 @@ export default function InviteUserModal({ isOpen, onOpenChange }: Readonly ( @@ -35,7 +34,7 @@ export function CompleteLoginForm() { const { email, loginId, expireAt } = getLoginState(); const { expiresInString, isExpired } = useExpirationTimeout(expireAt); - const [{ success, title, message, errors }, action] = useFormState( + const [{ success, title, message, errors }, action] = useActionState( api.actionPost("/api/account-management/authentication/login/{id}/complete"), { success: null diff --git a/application/account-management/WebApp/routes/signup/index.tsx b/application/account-management/WebApp/routes/signup/index.tsx index 78983bbf2..deec422b3 100644 --- a/application/account-management/WebApp/routes/signup/index.tsx +++ b/application/account-management/WebApp/routes/signup/index.tsx @@ -13,8 +13,7 @@ import logoMarkUrl from "@/shared/images/logo-mark.svg"; import poweredByUrl from "@/shared/images/powered-by.svg"; import { TextField } from "@repo/ui/components/TextField"; import { Form } from "@repo/ui/components/Form"; -import { useFormState } from "react-dom"; -import { useState } from "react"; +import { useActionState, useState } from "react"; import { api, useApi } from "@/shared/lib/api/client"; import { setSignupState } from "./-shared/signupState"; import { FormErrorMessage } from "@repo/ui/components/FormErrorMessage"; @@ -36,7 +35,7 @@ export const Route = createFileRoute("/signup/")({ export function StartSignupForm() { const [email, setEmail] = useState(""); - const [{ success, errors, data, title, message }, action, isPending] = useFormState( + const [{ success, errors, data, title, message }, action, isPending] = useActionState( api.actionPost("/api/account-management/signups/start"), { success: null } ); diff --git a/application/account-management/WebApp/routes/signup/verify.tsx b/application/account-management/WebApp/routes/signup/verify.tsx index 27cd14d7e..55e62388d 100644 --- a/application/account-management/WebApp/routes/signup/verify.tsx +++ b/application/account-management/WebApp/routes/signup/verify.tsx @@ -11,12 +11,11 @@ import { OneTimeCodeInput } from "@repo/ui/components/OneTimeCodeInput"; import { useExpirationTimeout } from "@repo/ui/hooks/useExpiration"; import logoMarkUrl from "@/shared/images/logo-mark.svg"; import poweredByUrl from "@/shared/images/powered-by.svg"; -import { useFormState } from "react-dom"; import { getSignupState } from "./-shared/signupState"; import { api } from "@/shared/lib/api/client"; import { FormErrorMessage } from "@repo/ui/components/FormErrorMessage"; import { signedUpPath } from "@repo/infrastructure/auth/constants"; -import { useEffect } from "react"; +import { useActionState, useEffect } from "react"; export const Route = createFileRoute("/signup/verify")({ component: () => ( @@ -35,7 +34,7 @@ export function CompleteSignupForm() { const { email, signupId, expireAt } = getSignupState(); const { expiresInString, isExpired } = useExpirationTimeout(expireAt); - const [{ success, title, message, errors }, action] = useFormState( + const [{ success, title, message, errors }, action] = useActionState( api.actionPost("/api/account-management/signups/{id}/complete"), { success: null diff --git a/application/account-management/WebApp/shared/components/userModals/UserProfileModal.tsx b/application/account-management/WebApp/shared/components/userModals/UserProfileModal.tsx index e7f6d09a4..0fe592497 100644 --- a/application/account-management/WebApp/shared/components/userModals/UserProfileModal.tsx +++ b/application/account-management/WebApp/shared/components/userModals/UserProfileModal.tsx @@ -1,5 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from "react"; -import { useFormState } from "react-dom"; +import { useActionState, useCallback, useEffect, useRef, useState } from "react"; import { FileTrigger, Form, Heading, Label } from "react-aria-components"; import { Menu, MenuItem, MenuSeparator, MenuTrigger } from "@repo/ui/components/Menu"; import { CameraIcon, Trash2Icon, XIcon } from "lucide-react"; @@ -63,7 +62,7 @@ export default function UserProfileModal({ isOpen, onOpenChange, userId }: Reado }, [onOpenChange, avatarPreviewUrl]); // Handle form submission - let [{ success, errors, title, message }, action, isPending] = useFormState( + let [{ success, errors, title, message }, action, isPending] = useActionState( api.actionPut("/api/account-management/users/{id}"), { success: null } ); From 1e8b7bf87090f69447999adc5c4922c73b3beb07 Mon Sep 17 00:00:00 2001 From: Thomas Jespersen Date: Fri, 27 Dec 2024 18:26:01 +0100 Subject: [PATCH 5/5] Fix missing Lingui macro imports to comply with v5 guidelines --- application/account-management/WebApp/routes/admin/index.tsx | 2 +- .../account-management/WebApp/routes/admin/users/index.tsx | 2 +- .../WebApp/routes/login/-shared/loginState.ts | 2 +- application/account-management/WebApp/routes/login/expired.tsx | 2 +- .../WebApp/routes/signup/-shared/signupState.ts | 2 +- application/account-management/WebApp/routes/signup/expired.tsx | 2 +- .../WebApp/shared/components/ErrorMessage.tsx | 2 +- .../account-management/WebApp/shared/components/HeroImage.tsx | 2 +- .../WebApp/shared/layouts/HorizontalHeroLayout.tsx | 2 +- application/back-office/WebApp/routes/back-office/index.tsx | 2 +- .../back-office/WebApp/shared/components/SharedSideMenu.tsx | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/application/account-management/WebApp/routes/admin/index.tsx b/application/account-management/WebApp/routes/admin/index.tsx index ef1105dff..785ef6a16 100644 --- a/application/account-management/WebApp/routes/admin/index.tsx +++ b/application/account-management/WebApp/routes/admin/index.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { Trans } from "@lingui/macro"; +import { Trans } from "@lingui/react/macro"; import { useApi } from "@/shared/lib/api/client"; import { TopMenu } from "@/shared/components/topMenu"; import { SharedSideMenu } from "@/shared/components/SharedSideMenu"; diff --git a/application/account-management/WebApp/routes/admin/users/index.tsx b/application/account-management/WebApp/routes/admin/users/index.tsx index 2db566b15..22cfcb69f 100644 --- a/application/account-management/WebApp/routes/admin/users/index.tsx +++ b/application/account-management/WebApp/routes/admin/users/index.tsx @@ -10,7 +10,7 @@ import { Button } from "@repo/ui/components/Button"; import { PlusIcon } from "lucide-react"; import { useState } from "react"; import InviteUserModal from "./-components/InviteUserModal"; -import { Trans } from "@lingui/macro"; +import { Trans } from "@lingui/react/macro"; const userPageSearchSchema = z.object({ pageOffset: z.number().default(0).optional(), diff --git a/application/account-management/WebApp/routes/login/-shared/loginState.ts b/application/account-management/WebApp/routes/login/-shared/loginState.ts index 8f67dfc5a..1cf287c7c 100644 --- a/application/account-management/WebApp/routes/login/-shared/loginState.ts +++ b/application/account-management/WebApp/routes/login/-shared/loginState.ts @@ -1,5 +1,5 @@ import type { Schemas } from "@/shared/lib/api/client"; -import { t } from "@lingui/macro"; +import { t } from "@lingui/core/macro"; interface LoginState { loginId: Schemas["LoginId"]; diff --git a/application/account-management/WebApp/routes/login/expired.tsx b/application/account-management/WebApp/routes/login/expired.tsx index b8d96dc7d..82a36b3eb 100644 --- a/application/account-management/WebApp/routes/login/expired.tsx +++ b/application/account-management/WebApp/routes/login/expired.tsx @@ -6,7 +6,7 @@ import { Link } from "@repo/ui/components/Link"; import { Content, Heading, IllustratedMessage } from "@repo/ui/components/IllustratedMessage"; import { getLoginState } from "./-shared/loginState"; import { loginPath } from "@repo/infrastructure/auth/constants"; -import { Trans } from "@lingui/macro"; +import { Trans } from "@lingui/react/macro"; export const Route = createFileRoute("/login/expired")({ component: () => ( diff --git a/application/account-management/WebApp/routes/signup/-shared/signupState.ts b/application/account-management/WebApp/routes/signup/-shared/signupState.ts index 787a705f3..63b536707 100644 --- a/application/account-management/WebApp/routes/signup/-shared/signupState.ts +++ b/application/account-management/WebApp/routes/signup/-shared/signupState.ts @@ -1,5 +1,5 @@ import type { Schemas } from "@/shared/lib/api/client"; -import { t } from "@lingui/macro"; +import { t } from "@lingui/core/macro"; interface SignupState { signupId: Schemas["SignupId"]; diff --git a/application/account-management/WebApp/routes/signup/expired.tsx b/application/account-management/WebApp/routes/signup/expired.tsx index b85f2d6e0..822a67de8 100644 --- a/application/account-management/WebApp/routes/signup/expired.tsx +++ b/application/account-management/WebApp/routes/signup/expired.tsx @@ -6,7 +6,7 @@ import { Link } from "@repo/ui/components/Link"; import { Content, Heading, IllustratedMessage } from "@repo/ui/components/IllustratedMessage"; import { getSignupState } from "./-shared/signupState"; import { signUpPath } from "@repo/infrastructure/auth/constants"; -import { Trans } from "@lingui/macro"; +import { Trans } from "@lingui/react/macro"; export const Route = createFileRoute("/signup/expired")({ component: () => ( diff --git a/application/account-management/WebApp/shared/components/ErrorMessage.tsx b/application/account-management/WebApp/shared/components/ErrorMessage.tsx index f842fd957..d633cc39f 100644 --- a/application/account-management/WebApp/shared/components/ErrorMessage.tsx +++ b/application/account-management/WebApp/shared/components/ErrorMessage.tsx @@ -3,7 +3,7 @@ import type { ErrorComponentProps } from "@tanstack/react-router"; import ErrorIllustration from "@spectrum-icons/illustrations/Error"; import { Content, Heading, IllustratedMessage } from "@repo/ui/components/IllustratedMessage"; import { Button } from "@repo/ui/components/Button"; -import { Trans } from "@lingui/macro"; +import { Trans } from "@lingui/react/macro"; export function ErrorMessage({ error, reset }: Readonly) { useEffect(() => { diff --git a/application/account-management/WebApp/shared/components/HeroImage.tsx b/application/account-management/WebApp/shared/components/HeroImage.tsx index 4900d85fd..885820490 100644 --- a/application/account-management/WebApp/shared/components/HeroImage.tsx +++ b/application/account-management/WebApp/shared/components/HeroImage.tsx @@ -1,4 +1,4 @@ -import { t } from "@lingui/macro"; +import { t } from "@lingui/core/macro"; import { Image } from "@repo/ui/components/Image"; import heroMobileBlurImage from "@/public/images/hero-mobile-blur.webp"; import heroDesktopBlurImage from "@/public/images/hero-desktop-blur.webp"; diff --git a/application/account-management/WebApp/shared/layouts/HorizontalHeroLayout.tsx b/application/account-management/WebApp/shared/layouts/HorizontalHeroLayout.tsx index bfdbe9477..0ed3e6df4 100644 --- a/application/account-management/WebApp/shared/layouts/HorizontalHeroLayout.tsx +++ b/application/account-management/WebApp/shared/layouts/HorizontalHeroLayout.tsx @@ -3,7 +3,7 @@ import { HeroImage } from "@/shared/components/HeroImage"; import { LocaleSwitcher } from "@repo/infrastructure/translations/LocaleSwitcher"; import { ThemeModeSelector } from "@repo/ui/theme/ThemeModeSelector"; import { Button } from "@repo/ui/components/Button"; -import { t } from "@lingui/macro"; +import { t } from "@lingui/core/macro"; import { LifeBuoyIcon } from "lucide-react"; interface HorizontalHeroLayoutProps { diff --git a/application/back-office/WebApp/routes/back-office/index.tsx b/application/back-office/WebApp/routes/back-office/index.tsx index 7f465eaab..9d7746a7d 100644 --- a/application/back-office/WebApp/routes/back-office/index.tsx +++ b/application/back-office/WebApp/routes/back-office/index.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; -import { Trans } from "@lingui/macro"; +import { Trans } from "@lingui/react/macro"; import { TopMenu } from "@/shared/components/topMenu"; import { SharedSideMenu } from "@/shared/components/SharedSideMenu"; diff --git a/application/back-office/WebApp/shared/components/SharedSideMenu.tsx b/application/back-office/WebApp/shared/components/SharedSideMenu.tsx index 91d64a90f..868a86f06 100644 --- a/application/back-office/WebApp/shared/components/SharedSideMenu.tsx +++ b/application/back-office/WebApp/shared/components/SharedSideMenu.tsx @@ -1,6 +1,6 @@ import { MenuButton, SideMenu, SideMenuSpacer } from "@repo/ui/components/SideMenu"; import { BoxIcon, HomeIcon } from "lucide-react"; -import { t } from "@lingui/macro"; +import { t } from "@lingui/core/macro"; type SharedSideMenuProps = { children?: React.ReactNode;