create-carlonicora-app 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +675 -0
- package/README.md +104 -0
- package/bin/cli.js +3 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +92 -0
- package/dist/cli.js.map +1 -0
- package/dist/git.d.ts +7 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +80 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts.d.ts +5 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +30 -0
- package/dist/prompts.js.map +1 -0
- package/dist/replacer.d.ts +9 -0
- package/dist/replacer.d.ts.map +1 -0
- package/dist/replacer.js +11 -0
- package/dist/replacer.js.map +1 -0
- package/dist/scaffold.d.ts +3 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +79 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/types/index.d.ts +16 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/files.d.ts +6 -0
- package/dist/utils/files.d.ts.map +1 -0
- package/dist/utils/files.js +103 -0
- package/dist/utils/files.js.map +1 -0
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +35 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/validation.d.ts +6 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +63 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +52 -0
- package/template/.env.example +159 -0
- package/template/.github/workflows/check-library-updates.yml +71 -0
- package/template/.github/workflows/dev.yml +63 -0
- package/template/.github/workflows/pull-request.yml +55 -0
- package/template/.gitmodules +6 -0
- package/template/.husky/pre-commit +1 -0
- package/template/.husky/pre-push +1 -0
- package/template/.prettierignore +1 -0
- package/template/.prettierrc +13 -0
- package/template/.releaserc +134 -0
- package/template/.vscode/settings.json +16 -0
- package/template/CHANGELOG.md +0 -0
- package/template/CLAUDE.md +34 -0
- package/template/DOCKER.md +1591 -0
- package/template/Dockerfile +228 -0
- package/template/README.md +1 -0
- package/template/apps/api/.prettierrc +12 -0
- package/template/apps/api/eslint.config.mjs +54 -0
- package/template/apps/api/jest.config.js +29 -0
- package/template/apps/api/nest-cli.json +15 -0
- package/template/apps/api/package.json +155 -0
- package/template/apps/api/src/config/config.ts +17 -0
- package/template/apps/api/src/config/enums/job.name.ts +6 -0
- package/template/apps/api/src/config/enums/queue.id.ts +3 -0
- package/template/apps/api/src/config/interfaces/config.interface.ts +3 -0
- package/template/apps/api/src/features/features.modules.ts +6 -0
- package/template/apps/api/src/i18n/en/notifications.json +3 -0
- package/template/apps/api/src/main.ts +23 -0
- package/template/apps/api/src/neo4j.migrations/20250901_001.ts +33 -0
- package/template/apps/api/src/neo4j.migrations/20250901_002.ts +90 -0
- package/template/apps/api/src/neo4j.migrations/20250901_003.ts +57 -0
- package/template/apps/api/src/neo4j.migrations/20250901_004.ts +32 -0
- package/template/apps/api/src/neo4j.migrations/queries/migration.queries.ts +49 -0
- package/template/apps/api/src/types/langchain.d.ts +56 -0
- package/template/apps/api/tsconfig.build.json +4 -0
- package/template/apps/api/tsconfig.json +38 -0
- package/template/apps/web/.swcrc +26 -0
- package/template/apps/web/components.json +21 -0
- package/template/apps/web/eslint.config.mjs +33 -0
- package/template/apps/web/global.d.ts +7 -0
- package/template/apps/web/messages/en.json +249 -0
- package/template/apps/web/next.config.js +50 -0
- package/template/apps/web/package.json +146 -0
- package/template/apps/web/playwright.config.ts +86 -0
- package/template/apps/web/postcss.config.mjs +5 -0
- package/template/apps/web/public/sw.js +32 -0
- package/template/apps/web/src/app/[locale]/(admin)/administration/companies/[id]/page.tsx +46 -0
- package/template/apps/web/src/app/[locale]/(admin)/administration/companies/page.tsx +23 -0
- package/template/apps/web/src/app/[locale]/(admin)/layout.tsx +49 -0
- package/template/apps/web/src/app/[locale]/(auth)/activation/[code]/page.tsx +13 -0
- package/template/apps/web/src/app/[locale]/(auth)/auth/page.tsx +11 -0
- package/template/apps/web/src/app/[locale]/(auth)/invitation/[code]/page.tsx +7 -0
- package/template/apps/web/src/app/[locale]/(auth)/layout.tsx +9 -0
- package/template/apps/web/src/app/[locale]/(auth)/login/page.tsx +6 -0
- package/template/apps/web/src/app/[locale]/(auth)/logout/page.tsx +5 -0
- package/template/apps/web/src/app/[locale]/(auth)/register/page.tsx +6 -0
- package/template/apps/web/src/app/[locale]/(auth)/reset/[code]/page.tsx +7 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/notifications/page.tsx +9 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/roles/[id]/page.tsx +23 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/roles/page.tsx +12 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/users/[id]/error.tsx +14 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/users/[id]/loading.tsx +21 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/users/[id]/page.tsx +46 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/users/page.tsx +17 -0
- package/template/apps/web/src/app/[locale]/(main)/error.tsx +62 -0
- package/template/apps/web/src/app/[locale]/(main)/layout.tsx +40 -0
- package/template/apps/web/src/app/[locale]/(main)/page.tsx +41 -0
- package/template/apps/web/src/app/[locale]/layout.tsx +54 -0
- package/template/apps/web/src/app/globals.css +256 -0
- package/template/apps/web/src/config/BootstrapProvider.tsx +13 -0
- package/template/apps/web/src/config/Bootstrapper.ts +77 -0
- package/template/apps/web/src/config/env.ts +51 -0
- package/template/apps/web/src/config/middleware-env.ts +14 -0
- package/template/apps/web/src/enums/feature.ids.ts +3 -0
- package/template/apps/web/src/features/common/components/containers/IndexContainer.tsx +11 -0
- package/template/apps/web/src/features/common/components/details/LayoutDetails.tsx +33 -0
- package/template/apps/web/src/features/common/components/navigations/CommonSidebar.tsx +233 -0
- package/template/apps/web/src/features/common/components/navigations/CreationDropDown.tsx +117 -0
- package/template/apps/web/src/features/common/components/navigations/UserSidebarFooter.tsx +115 -0
- package/template/apps/web/src/features/common/components/navigations/VersionDisplay.tsx +18 -0
- package/template/apps/web/src/features/common/contexts/ErrorContext.tsx +62 -0
- package/template/apps/web/src/i18n/request.ts +13 -0
- package/template/apps/web/src/i18n/routing.ts +9 -0
- package/template/apps/web/src/i18n/useDateFnsLocale.ts +15 -0
- package/template/apps/web/src/proxy.ts +107 -0
- package/template/apps/web/src/server-actions/auth-cookies.ts +134 -0
- package/template/apps/web/src/types/modules.d.ts +10 -0
- package/template/apps/web/src/utils/metadata.ts +50 -0
- package/template/apps/web/src/utils/revalidation.ts +7 -0
- package/template/apps/web/tsconfig.json +51 -0
- package/template/docker-compose.yml +211 -0
- package/template/package.json +72 -0
- package/template/packages/nestjs-neo4jsonapi/.gitkeep +0 -0
- package/template/packages/nextjs-jsonapi/.gitkeep +0 -0
- package/template/packages/shared/package.json +23 -0
- package/template/packages/shared/src/const/roles.id.ts +5 -0
- package/template/packages/shared/src/const/system.roles.id.ts +4 -0
- package/template/packages/shared/src/index.ts +1 -0
- package/template/packages/shared/tsconfig.json +10 -0
- package/template/packages/shared/tsup.config.ts +16 -0
- package/template/pnpm-workspace.yaml +3 -0
- package/template/tsconfig.base.json +62 -0
- package/template/tsconfig.json +12 -0
- package/template/turbo.json +88 -0
- package/template/versions.production.json +4 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PageContainer, UsersListContainer } from "@carlonicora/nextjs-jsonapi/components";
|
|
2
|
+
import { UserProvider } from "@carlonicora/nextjs-jsonapi/contexts";
|
|
3
|
+
import { Modules } from "@carlonicora/nextjs-jsonapi/core";
|
|
4
|
+
import { Action } from "@carlonicora/nextjs-jsonapi/permissions";
|
|
5
|
+
import { ServerSession } from "@carlonicora/nextjs-jsonapi/server";
|
|
6
|
+
|
|
7
|
+
export default async function UsersListPage() {
|
|
8
|
+
ServerSession.checkPermission({ module: Modules.User, action: Action.Read });
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<UserProvider>
|
|
12
|
+
<PageContainer testId="page-users-container">
|
|
13
|
+
<UsersListContainer />
|
|
14
|
+
</PageContainer>
|
|
15
|
+
</UserProvider>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { ErrorDetails, Logout } from "@carlonicora/nextjs-jsonapi/components";
|
|
4
|
+
import { Button } from "@carlonicora/nextjs-jsonapi/shadcnui";
|
|
5
|
+
import { useMessages } from "next-intl";
|
|
6
|
+
import { useEffect } from "react";
|
|
7
|
+
|
|
8
|
+
interface ErrorProps {
|
|
9
|
+
error: Error & { status?: number; digest?: string };
|
|
10
|
+
reset: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default function Error({ error, reset }: ErrorProps) {
|
|
14
|
+
const messages = useMessages() as any;
|
|
15
|
+
|
|
16
|
+
const [status, message] = error.message.split(`:`) as any[];
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
console.error("Application error:", error);
|
|
20
|
+
}, [error]);
|
|
21
|
+
|
|
22
|
+
if ((error.status ?? +status) === 401) {
|
|
23
|
+
return <Logout />;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if ((error.status ?? +status) === 404) {
|
|
27
|
+
return <ErrorPage code={404} reset={reset} messages={messages} />;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if ((error.status ?? +status) === 500 || (error.status ?? +status) === 503) {
|
|
31
|
+
return <ErrorPage code={error.status ?? status} reset={reset} messages={messages} />;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className="flex min-h-screen w-full flex-col items-center justify-center">
|
|
36
|
+
<ErrorDetails
|
|
37
|
+
code={error.status ?? +status}
|
|
38
|
+
title={`Something went wrong!`}
|
|
39
|
+
message={message ?? error.message ?? "An unexpected error occurred."}
|
|
40
|
+
/>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function ErrorPage({ code, reset, messages }: { code: number; reset: () => void; messages: any }) {
|
|
46
|
+
const errorMessages = messages?.errors?.[code.toString()];
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div className="flex min-h-screen w-full flex-col items-center justify-center">
|
|
50
|
+
<ErrorDetails
|
|
51
|
+
code={code}
|
|
52
|
+
title={errorMessages?.title || `Error ${code}`}
|
|
53
|
+
message={errorMessages?.message || "An error occurred."}
|
|
54
|
+
/>
|
|
55
|
+
<div className="mt-4">
|
|
56
|
+
<Button onClick={reset} variant="default">
|
|
57
|
+
Try again
|
|
58
|
+
</Button>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ServerSession } from "@carlonicora/nextjs-jsonapi/server";
|
|
2
|
+
import "react-horizontal-scrolling-menu/dist/styles.css";
|
|
3
|
+
|
|
4
|
+
import LayoutDetails from "@/features/common/components/details/LayoutDetails";
|
|
5
|
+
import { AuthContainer, PushNotificationProvider, RefreshUser } from "@carlonicora/nextjs-jsonapi/components";
|
|
6
|
+
import { NotificationContextProvider, SocketProvider } from "@carlonicora/nextjs-jsonapi/contexts";
|
|
7
|
+
import { AuthComponent } from "@carlonicora/nextjs-jsonapi/features";
|
|
8
|
+
import { SidebarProvider } from "@carlonicora/nextjs-jsonapi/shadcnui";
|
|
9
|
+
import { cookies } from "next/headers";
|
|
10
|
+
|
|
11
|
+
export default async function MainLayout(props: { children: React.ReactNode; params: Promise<{ locale: string }> }) {
|
|
12
|
+
const { children } = props;
|
|
13
|
+
|
|
14
|
+
const cookieStore = await cookies();
|
|
15
|
+
const token = cookieStore.get("token")?.value;
|
|
16
|
+
const defaultOpen = cookieStore.get("sidebar_state")?.value === "true";
|
|
17
|
+
|
|
18
|
+
if (!(await ServerSession.isLogged()))
|
|
19
|
+
return (
|
|
20
|
+
<div className="flex min-h-screen w-full flex-col items-center justify-center">
|
|
21
|
+
<AuthContainer componentType={AuthComponent.Landing} />
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
// if (await ServerSession.isLicenseActive())
|
|
26
|
+
return (
|
|
27
|
+
<SocketProvider token={token}>
|
|
28
|
+
<PushNotificationProvider>
|
|
29
|
+
<NotificationContextProvider>
|
|
30
|
+
<SidebarProvider defaultOpen={defaultOpen}>
|
|
31
|
+
<RefreshUser />
|
|
32
|
+
<LayoutDetails>{children}</LayoutDetails>
|
|
33
|
+
</SidebarProvider>
|
|
34
|
+
</NotificationContextProvider>
|
|
35
|
+
</PushNotificationProvider>
|
|
36
|
+
</SocketProvider>
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// return <CompanyLicense />;
|
|
40
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import IndexContainer from "@/features/common/components/containers/IndexContainer";
|
|
2
|
+
import { generateSpecificMetadata } from "@/utils/metadata";
|
|
3
|
+
import { AuthContainer, CompaniesList, PageContainer } from "@carlonicora/nextjs-jsonapi/components";
|
|
4
|
+
import { CommonProvider, CompanyProvider } from "@carlonicora/nextjs-jsonapi/contexts";
|
|
5
|
+
import { AuthComponent, ContentInterface, ContentService } from "@carlonicora/nextjs-jsonapi/features";
|
|
6
|
+
import { ServerSession } from "@carlonicora/nextjs-jsonapi/server";
|
|
7
|
+
import { RoleId } from "@{{name}}/shared";
|
|
8
|
+
import { Metadata } from "next";
|
|
9
|
+
import { getTranslations } from "next-intl/server";
|
|
10
|
+
|
|
11
|
+
export async function generateMetadata(): Promise<Metadata> {
|
|
12
|
+
const t = await getTranslations();
|
|
13
|
+
|
|
14
|
+
return await generateSpecificMetadata({ title: t(`generic.home`) });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default async function IndexPage() {
|
|
18
|
+
if (!(await ServerSession.isLogged())) return <AuthContainer componentType={AuthComponent.Landing} />;
|
|
19
|
+
|
|
20
|
+
if (await ServerSession.hasRole(RoleId.Administrator)) {
|
|
21
|
+
return (
|
|
22
|
+
<CompanyProvider>
|
|
23
|
+
<CommonProvider>
|
|
24
|
+
<PageContainer>
|
|
25
|
+
<CompaniesList />
|
|
26
|
+
</PageContainer>
|
|
27
|
+
</CommonProvider>
|
|
28
|
+
</CompanyProvider>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const contentList: ContentInterface[] = await ContentService.findMany({});
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<CommonProvider>
|
|
36
|
+
<PageContainer testId="page-homepage-container">
|
|
37
|
+
<IndexContainer dehydratedContentList={contentList ? contentList.map((content) => content.dehydrate()) : []} />
|
|
38
|
+
</PageContainer>
|
|
39
|
+
</CommonProvider>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { BootstrapProvider } from "@/config/BootstrapProvider";
|
|
2
|
+
import "@/config/env"; // Server-side bootstrap
|
|
3
|
+
import { CurrentUserProvider } from "@carlonicora/nextjs-jsonapi/contexts";
|
|
4
|
+
import { Provider } from "jotai";
|
|
5
|
+
import "react-horizontal-scrolling-menu/dist/styles.css";
|
|
6
|
+
|
|
7
|
+
import { routing } from "@/i18n/routing";
|
|
8
|
+
// import { cn } from "@/lib/utils";
|
|
9
|
+
import { TooltipProvider } from "@carlonicora/nextjs-jsonapi/shadcnui";
|
|
10
|
+
import { cn } from "@carlonicora/nextjs-jsonapi/utils";
|
|
11
|
+
import { hasLocale, NextIntlClientProvider } from "next-intl";
|
|
12
|
+
import { getMessages, setRequestLocale } from "next-intl/server";
|
|
13
|
+
import { ThemeProvider } from "next-themes";
|
|
14
|
+
import { Inter } from "next/font/google";
|
|
15
|
+
import { notFound } from "next/navigation";
|
|
16
|
+
import { Toaster } from "sonner";
|
|
17
|
+
import "../globals.css";
|
|
18
|
+
|
|
19
|
+
const fontSans = Inter({ subsets: ["latin"], weight: ["100", "300", "400", "700"], variable: "--font-sans" });
|
|
20
|
+
|
|
21
|
+
export default async function RootLayout(props: { children: React.ReactNode; params: Promise<{ locale: string }> }) {
|
|
22
|
+
const params = await props.params;
|
|
23
|
+
|
|
24
|
+
const { locale } = params;
|
|
25
|
+
const { children } = props;
|
|
26
|
+
|
|
27
|
+
if (!hasLocale(routing.locales, locale)) {
|
|
28
|
+
notFound();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setRequestLocale(locale);
|
|
32
|
+
const messages = await getMessages();
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<html suppressHydrationWarning lang={locale}>
|
|
36
|
+
<body className={cn("bg-background top-0! min-h-screen font-sans antialiased", fontSans.variable)}>
|
|
37
|
+
<Provider>
|
|
38
|
+
<ThemeProvider attribute="class" defaultTheme="light" enableSystem disableTransitionOnChange>
|
|
39
|
+
<NextIntlClientProvider messages={messages}>
|
|
40
|
+
<BootstrapProvider>
|
|
41
|
+
<CurrentUserProvider>
|
|
42
|
+
<TooltipProvider>
|
|
43
|
+
<Toaster closeButton richColors />
|
|
44
|
+
{children}
|
|
45
|
+
</TooltipProvider>
|
|
46
|
+
</CurrentUserProvider>
|
|
47
|
+
</BootstrapProvider>
|
|
48
|
+
</NextIntlClientProvider>
|
|
49
|
+
</ThemeProvider>
|
|
50
|
+
</Provider>
|
|
51
|
+
</body>
|
|
52
|
+
</html>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@import "tw-animate-css";
|
|
3
|
+
|
|
4
|
+
/* Include package source files for Tailwind to scan */
|
|
5
|
+
/* Development: workspace package */
|
|
6
|
+
@source "../../../../packages/nextjs-jsonapi/src/**/*.{ts,tsx}";
|
|
7
|
+
/* Production: npm package in node_modules (2 levels from apps/web/src/app/) */
|
|
8
|
+
@source "../../node_modules/@carlonicora/nextjs-jsonapi/src/**/*.{ts,tsx}";
|
|
9
|
+
|
|
10
|
+
@custom-variant dark (&:is(.dark *));
|
|
11
|
+
|
|
12
|
+
@theme inline {
|
|
13
|
+
--color-background: var(--background);
|
|
14
|
+
--color-foreground: var(--foreground);
|
|
15
|
+
--font-sans: var(--font-geist-sans);
|
|
16
|
+
--font-mono: var(--font-geist-mono);
|
|
17
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
18
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
19
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
20
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
21
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
22
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
23
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
24
|
+
--color-sidebar: var(--sidebar);
|
|
25
|
+
--color-chart-5: var(--chart-5);
|
|
26
|
+
--color-chart-4: var(--chart-4);
|
|
27
|
+
--color-chart-3: var(--chart-3);
|
|
28
|
+
--color-chart-2: var(--chart-2);
|
|
29
|
+
--color-chart-1: var(--chart-1);
|
|
30
|
+
--color-ring: var(--ring);
|
|
31
|
+
--color-input: var(--input);
|
|
32
|
+
--color-border: var(--border);
|
|
33
|
+
--color-destructive: var(--destructive);
|
|
34
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
35
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
36
|
+
--color-accent: var(--accent);
|
|
37
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
38
|
+
--color-muted: var(--muted);
|
|
39
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
40
|
+
--color-secondary: var(--secondary);
|
|
41
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
42
|
+
--color-primary: var(--primary);
|
|
43
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
44
|
+
--color-popover: var(--popover);
|
|
45
|
+
--color-card-foreground: var(--card-foreground);
|
|
46
|
+
--color-card: var(--card);
|
|
47
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
48
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
49
|
+
--radius-lg: var(--radius);
|
|
50
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
51
|
+
--color-warning: var(--warning);
|
|
52
|
+
--color-warning-foreground: var(--warning-foreground);
|
|
53
|
+
--radius: var(--radius);
|
|
54
|
+
--shadow-opacity: var(--shadow-opacity);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
:root {
|
|
58
|
+
--radius: 0.625rem;
|
|
59
|
+
--background: oklch(1 0 0);
|
|
60
|
+
--foreground: oklch(0.145 0 0);
|
|
61
|
+
--card: oklch(1 0 0);
|
|
62
|
+
--card-foreground: oklch(0.145 0 0);
|
|
63
|
+
--popover: oklch(1 0 0);
|
|
64
|
+
--popover-foreground: oklch(0.145 0 0);
|
|
65
|
+
--primary: oklch(0.205 0 0);
|
|
66
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
67
|
+
--secondary: oklch(0.97 0 0);
|
|
68
|
+
--secondary-foreground: oklch(0.205 0 0);
|
|
69
|
+
--muted: oklch(0.97 0 0);
|
|
70
|
+
--muted-foreground: oklch(0.556 0 0);
|
|
71
|
+
--accent: oklch(0.59 0.0775 253.92);
|
|
72
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
73
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
74
|
+
--destructive-foreground: oklch(0.985 0 0);
|
|
75
|
+
--border: oklch(0.922 0 0);
|
|
76
|
+
--input: oklch(0.922 0 0);
|
|
77
|
+
--ring: oklch(0.708 0 0);
|
|
78
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
79
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
80
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
81
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
82
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
83
|
+
--sidebar: oklch(0.985 0 0);
|
|
84
|
+
--sidebar-foreground: oklch(0.145 0 0);
|
|
85
|
+
--sidebar-primary: oklch(0.205 0 0);
|
|
86
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
87
|
+
--sidebar-accent: oklch(0.97 0 0);
|
|
88
|
+
--sidebar-accent-foreground: oklch(0.205 0 0);
|
|
89
|
+
--sidebar-border: oklch(0.922 0 0);
|
|
90
|
+
--sidebar-ring: oklch(0.708 0 0);
|
|
91
|
+
--warning: oklch(0.84 0.16 84);
|
|
92
|
+
--warning-foreground: oklch(0.28 0.07 46);
|
|
93
|
+
--radius: 0.6rem;
|
|
94
|
+
--shadow-opacity: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.dark {
|
|
98
|
+
--background: oklch(0.145 0 0);
|
|
99
|
+
--foreground: oklch(0.985 0 0);
|
|
100
|
+
--card: oklch(0.205 0 0);
|
|
101
|
+
--card-foreground: oklch(0.985 0 0);
|
|
102
|
+
--popover: oklch(0.269 0 0);
|
|
103
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
104
|
+
--primary: oklch(0.922 0 0);
|
|
105
|
+
--primary-foreground: oklch(0.205 0 0);
|
|
106
|
+
--secondary: oklch(0.269 0 0);
|
|
107
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
108
|
+
--muted: oklch(0.269 0 0);
|
|
109
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
110
|
+
--accent: oklch(0.59 0.0775 253.92);
|
|
111
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
112
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
113
|
+
--destructive-foreground: oklch(0.985 0 0);
|
|
114
|
+
--border: oklch(1 0 0 / 10%);
|
|
115
|
+
--input: oklch(1 0 0 / 15%);
|
|
116
|
+
--ring: oklch(0.556 0 0);
|
|
117
|
+
--chart-1: oklch(0.488 0.243 264.376);
|
|
118
|
+
--chart-2: oklch(0.696 0.17 162.48);
|
|
119
|
+
--chart-3: oklch(0.769 0.188 70.08);
|
|
120
|
+
--chart-4: oklch(0.627 0.265 303.9);
|
|
121
|
+
--chart-5: oklch(0.645 0.246 16.439);
|
|
122
|
+
--sidebar: oklch(0.205 0 0);
|
|
123
|
+
--sidebar-foreground: oklch(0.985 0 0);
|
|
124
|
+
--sidebar-primary: oklch(0.488 0.243 264.376);
|
|
125
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
126
|
+
--sidebar-accent: oklch(0.269 0 0);
|
|
127
|
+
--sidebar-accent-foreground: oklch(0.985 0 0);
|
|
128
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
129
|
+
--sidebar-ring: oklch(0.439 0 0);
|
|
130
|
+
--warning: oklch(0.41 0.11 46);
|
|
131
|
+
--warning-foreground: oklch(0.99 0.02 95);
|
|
132
|
+
--radius: 0.6rem;
|
|
133
|
+
--shadow-opacity: 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
:root {
|
|
137
|
+
font-size: 14px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@layer base {
|
|
141
|
+
* {
|
|
142
|
+
@apply border-border;
|
|
143
|
+
overscroll-behavior-y: none;
|
|
144
|
+
}
|
|
145
|
+
body {
|
|
146
|
+
@apply bg-background text-foreground;
|
|
147
|
+
overscroll-behavior-y: none;
|
|
148
|
+
-webkit-overflow-scrolling: touch;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.BlockNoteView .bn-editor[contenteditable="false"] {
|
|
153
|
+
padding-inline: 0px !important;
|
|
154
|
+
background-color: transparent !important;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.BlockNoteView .bn-editor[contenteditable="true"] {
|
|
158
|
+
@apply text-sm;
|
|
159
|
+
padding-inline: 24px !important;
|
|
160
|
+
background-color: transparent !important;
|
|
161
|
+
height: 100%;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.bn-block-content h1 {
|
|
165
|
+
@apply !text-3xl !font-bold;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.bn-block-content h2 {
|
|
169
|
+
@apply !text-2xl !font-bold;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.bn-block-content h3 {
|
|
173
|
+
@apply !text-lg !font-bold;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.bn-side-menu svg {
|
|
177
|
+
width: 16px;
|
|
178
|
+
height: 16px;
|
|
179
|
+
transform: scale(1);
|
|
180
|
+
transform-origin: center;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.BlockNoteView.small .bn-editor {
|
|
184
|
+
font-size: small !important;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.ProseMirror-focused {
|
|
188
|
+
outline: none !important;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@layer utilities {
|
|
192
|
+
.animate-spin-slow {
|
|
193
|
+
animation: spin 3s linear infinite !important;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* BlockNote diff word styling using data attribute */
|
|
198
|
+
[data-diff="added"] {
|
|
199
|
+
@apply rounded bg-green-50 px-1 font-bold text-green-800;
|
|
200
|
+
}
|
|
201
|
+
[data-diff="removed"] {
|
|
202
|
+
@apply rounded bg-red-50 px-1 text-red-800 line-through;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* @layer base {
|
|
206
|
+
* {
|
|
207
|
+
@apply border-border outline-ring/50;
|
|
208
|
+
}
|
|
209
|
+
body {
|
|
210
|
+
@apply bg-background text-foreground;
|
|
211
|
+
}
|
|
212
|
+
} */
|
|
213
|
+
|
|
214
|
+
.BlockNoteView .bn-suggestion-menu {
|
|
215
|
+
@apply bg-popover;
|
|
216
|
+
border-radius: 8px;
|
|
217
|
+
box-shadow:
|
|
218
|
+
0px 4px 6px -1px rgba(0, 0, 0, 0.1),
|
|
219
|
+
0px 2px 4px -2px rgba(0, 0, 0, 0.05);
|
|
220
|
+
max-height: 320px !important;
|
|
221
|
+
overflow-y: auto !important;
|
|
222
|
+
min-width: 320px !important;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.BlockNoteView .bn-suggestion-menu-item {
|
|
226
|
+
@apply text-xs;
|
|
227
|
+
transition: background-color 0.2s ease !important;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.BlockNoteView .bn-suggestion-menu-item:hover,
|
|
231
|
+
.BlockNoteView .bn-suggestion-menu-item[aria-selected="true"] {
|
|
232
|
+
@apply bg-accent text-accent-foreground;
|
|
233
|
+
}
|
|
234
|
+
/*
|
|
235
|
+
.BlockNoteView .bn-suggestion-menu-item .bn-text-base {
|
|
236
|
+
@apply text-xs;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.BlockNoteView .bn-suggestion-menu-item .bn-text-xs {
|
|
240
|
+
@apply: text-accent font-xs;
|
|
241
|
+
} */
|
|
242
|
+
|
|
243
|
+
.BlockNoteView .bn-suggestion-menu::-webkit-scrollbar {
|
|
244
|
+
width: 8px !important;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.BlockNoteView .bn-suggestion-menu::-webkit-scrollbar-track {
|
|
248
|
+
/* background: #1a1a1a !important; */
|
|
249
|
+
@apply bg-accent;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.BlockNoteView .bn-suggestion-menu::-webkit-scrollbar-thumb {
|
|
253
|
+
/* background-color: #444 !important; */
|
|
254
|
+
/* background-color: var(--color-border); */
|
|
255
|
+
border-radius: 4px !important;
|
|
256
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// Import env.ts to run all configurations on the client side:
|
|
4
|
+
// - bootstrap() for ModuleRegistry
|
|
5
|
+
// - configureJsonApi() for API client
|
|
6
|
+
// - configureAuth() for token handling (login/logout)
|
|
7
|
+
// - configureI18n() for translations
|
|
8
|
+
// - configureRoles() for role IDs
|
|
9
|
+
import "@/config/env";
|
|
10
|
+
|
|
11
|
+
export function BootstrapProvider({ children }: { children: React.ReactNode }) {
|
|
12
|
+
return <>{children}</>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { FeatureIds } from "@/enums/feature.ids";
|
|
2
|
+
import { DataClassRegistry, FieldSelector, ModuleRegistry } from "@carlonicora/nextjs-jsonapi/core";
|
|
3
|
+
import { ModuleWithPermissions } from "@carlonicora/nextjs-jsonapi/permissions";
|
|
4
|
+
import { LucideIcon } from "lucide-react";
|
|
5
|
+
|
|
6
|
+
// Foundation module imports (from app - code stays in app)
|
|
7
|
+
import {
|
|
8
|
+
AuthModule,
|
|
9
|
+
AuthorModule,
|
|
10
|
+
CompanyModule,
|
|
11
|
+
ContentModule,
|
|
12
|
+
FeatureModule,
|
|
13
|
+
ModuleModule,
|
|
14
|
+
NotificationModule,
|
|
15
|
+
PushModule,
|
|
16
|
+
RoleModule,
|
|
17
|
+
S3Module,
|
|
18
|
+
UserModule,
|
|
19
|
+
} from "@carlonicora/nextjs-jsonapi/features";
|
|
20
|
+
|
|
21
|
+
// Feature module imports
|
|
22
|
+
|
|
23
|
+
const moduleFactory = (params: {
|
|
24
|
+
pageUrl?: string;
|
|
25
|
+
name: string;
|
|
26
|
+
cache?: string;
|
|
27
|
+
model: any;
|
|
28
|
+
feature?: FeatureIds;
|
|
29
|
+
moduleId?: string;
|
|
30
|
+
icon?: LucideIcon;
|
|
31
|
+
inclusions?: Record<string, { types?: string[]; fields?: FieldSelector<any>[] }>;
|
|
32
|
+
}): ModuleWithPermissions => ({
|
|
33
|
+
pageUrl: params.pageUrl,
|
|
34
|
+
name: params.name,
|
|
35
|
+
model: params.model,
|
|
36
|
+
feature: params.feature,
|
|
37
|
+
moduleId: params.moduleId,
|
|
38
|
+
cache: params.cache,
|
|
39
|
+
icon: params.icon,
|
|
40
|
+
inclusions: params.inclusions ?? {},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// SINGLE SOURCE OF TRUTH: Define ALL modules ONCE as object
|
|
44
|
+
// TypeScript infers types from this object
|
|
45
|
+
const allModules = {
|
|
46
|
+
// Foundation modules (types defined in library, code in app except S3)
|
|
47
|
+
Auth: AuthModule(moduleFactory),
|
|
48
|
+
Company: CompanyModule(moduleFactory),
|
|
49
|
+
Feature: FeatureModule(moduleFactory),
|
|
50
|
+
Module: ModuleModule(moduleFactory),
|
|
51
|
+
Notification: NotificationModule(moduleFactory),
|
|
52
|
+
Push: PushModule(moduleFactory),
|
|
53
|
+
Role: RoleModule(moduleFactory),
|
|
54
|
+
S3: S3Module(moduleFactory),
|
|
55
|
+
User: UserModule(moduleFactory),
|
|
56
|
+
Author: AuthorModule(moduleFactory),
|
|
57
|
+
Content: ContentModule(moduleFactory),
|
|
58
|
+
} satisfies Record<string, ModuleWithPermissions>;
|
|
59
|
+
|
|
60
|
+
// Export type derived from the object - NO DUPLICATION
|
|
61
|
+
export type AllModuleDefinitions = typeof allModules;
|
|
62
|
+
|
|
63
|
+
let bootstrapped = false;
|
|
64
|
+
|
|
65
|
+
export function bootstrap(): void {
|
|
66
|
+
if (bootstrapped) return;
|
|
67
|
+
|
|
68
|
+
// Register ALL modules from the single source object
|
|
69
|
+
Object.entries(allModules).forEach(([name, module]) => {
|
|
70
|
+
ModuleRegistry.register(name, module);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Register model classes for JSON:API response translation
|
|
74
|
+
DataClassRegistry.bootstrap(allModules);
|
|
75
|
+
|
|
76
|
+
bootstrapped = true;
|
|
77
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { bootstrap } from "@/config/Bootstrapper";
|
|
2
|
+
import { ENV } from "@/config/middleware-env";
|
|
3
|
+
import { Link, usePathname, useRouter } from "@/i18n/routing";
|
|
4
|
+
import { useDateFnsLocale } from "@/i18n/useDateFnsLocale";
|
|
5
|
+
import { removeToken, updateToken } from "@/server-actions/auth-cookies";
|
|
6
|
+
import { configureAuth, configureI18n, configureJsonApi, configureRoles, Modules } from "@carlonicora/nextjs-jsonapi";
|
|
7
|
+
import { RoleId } from "@{{name}}/shared";
|
|
8
|
+
import { useLocale, useTranslations } from "next-intl";
|
|
9
|
+
|
|
10
|
+
// Re-export ENV for use by non-middleware code
|
|
11
|
+
export { ENV };
|
|
12
|
+
|
|
13
|
+
// Bootstrap modules immediately when this file is imported
|
|
14
|
+
// This ensures modules are registered before any component accesses Modules
|
|
15
|
+
bootstrap();
|
|
16
|
+
|
|
17
|
+
// Validate required env vars
|
|
18
|
+
if (!ENV.API_URL) {
|
|
19
|
+
throw new Error("NEXT_PUBLIC_API_URL is required but not set");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (!ENV.APP_URL) {
|
|
23
|
+
throw new Error("NEXT_PUBLIC_ADDRESS is required but not set");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Configure JSON:API client
|
|
27
|
+
configureJsonApi({
|
|
28
|
+
apiUrl: ENV.API_URL,
|
|
29
|
+
appUrl: ENV.APP_URL,
|
|
30
|
+
trackablePages: [Modules.Notification, Modules.Company, Modules.User],
|
|
31
|
+
bootstrapper: bootstrap,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Configure auth token handling (Server Actions for cookie management)
|
|
35
|
+
configureAuth({
|
|
36
|
+
updateToken,
|
|
37
|
+
removeToken,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Configure i18n (router, translations, link component, locale)
|
|
41
|
+
configureI18n({
|
|
42
|
+
useRouter,
|
|
43
|
+
useTranslations,
|
|
44
|
+
useLocale,
|
|
45
|
+
useDateFnsLocale,
|
|
46
|
+
Link,
|
|
47
|
+
usePathname,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Configure role IDs for role-based access control
|
|
51
|
+
configureRoles(RoleId);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware-safe environment configuration
|
|
3
|
+
* This file ONLY exports ENV constants without any library imports
|
|
4
|
+
* It's safe to use in Next.js middleware which has restricted module support
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export const ENV = {
|
|
8
|
+
API_URL:
|
|
9
|
+
(typeof window === "undefined" ? process.env.API_INTERNAL_URL : undefined) || process.env.NEXT_PUBLIC_API_URL!,
|
|
10
|
+
APP_URL: process.env.NEXT_PUBLIC_ADDRESS
|
|
11
|
+
? process.env.NEXT_PUBLIC_ADDRESS.trim().replace(/\/+$/, "") // Trim whitespace & remove trailing slashes
|
|
12
|
+
: "",
|
|
13
|
+
VAPID_PUBLIC_KEY: process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!,
|
|
14
|
+
} as const;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { JsonApiHydratedDataInterface } from "@carlonicora/nextjs-jsonapi/core";
|
|
4
|
+
|
|
5
|
+
type IndexContainerProps = {
|
|
6
|
+
dehydratedContentList: JsonApiHydratedDataInterface[];
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default function IndexContainer({ dehydratedContentList }: IndexContainerProps) {
|
|
10
|
+
return <div>index</div>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import CommonSidebar from "@/features/common/components/navigations/CommonSidebar";
|
|
4
|
+
import { useCurrentUserContext, useNotificationContext } from "@carlonicora/nextjs-jsonapi/contexts";
|
|
5
|
+
import { RoleInterface, UserInterface } from "@carlonicora/nextjs-jsonapi/features";
|
|
6
|
+
import { useNotificationSync, usePageTracker } from "@carlonicora/nextjs-jsonapi/hooks";
|
|
7
|
+
import { RoleId } from "@{{name}}/shared";
|
|
8
|
+
import { useEffect } from "react";
|
|
9
|
+
|
|
10
|
+
type LayoutDetailsProps = { children: React.ReactNode };
|
|
11
|
+
|
|
12
|
+
export default function LayoutDetails({ children }: LayoutDetailsProps) {
|
|
13
|
+
// Notification functionality enabled
|
|
14
|
+
|
|
15
|
+
const { currentUser } = useCurrentUserContext<UserInterface>();
|
|
16
|
+
const { loadNotifications } = useNotificationContext();
|
|
17
|
+
|
|
18
|
+
useNotificationSync();
|
|
19
|
+
usePageTracker();
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (currentUser && !currentUser.roles?.find((role: RoleInterface) => role.id === RoleId.Administrator)) {
|
|
23
|
+
loadNotifications();
|
|
24
|
+
}
|
|
25
|
+
}, [currentUser, loadNotifications]);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div data-wrapper className="flex h-full w-full">
|
|
29
|
+
<CommonSidebar />
|
|
30
|
+
<div className="flex flex-1 flex-col">{children}</div>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|