@carlonicora/nextjs-jsonapi 1.19.0 → 1.19.1

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.
Files changed (50) hide show
  1. package/dist/scripts/generate-web-module/generator.d.ts.map +1 -1
  2. package/dist/scripts/generate-web-module/generator.js +5 -71
  3. package/dist/scripts/generate-web-module/generator.js.map +1 -1
  4. package/dist/scripts/generate-web-module/templates/index.d.ts +0 -8
  5. package/dist/scripts/generate-web-module/templates/index.d.ts.map +1 -1
  6. package/dist/scripts/generate-web-module/templates/index.js +1 -18
  7. package/dist/scripts/generate-web-module/templates/index.js.map +1 -1
  8. package/package.json +1 -1
  9. package/scripts/generate-web-module/generator.ts +23 -111
  10. package/scripts/generate-web-module/templates/index.ts +0 -10
  11. package/dist/scripts/generate-web-module/templates/project/bootstrapper.template.d.ts +0 -7
  12. package/dist/scripts/generate-web-module/templates/project/bootstrapper.template.d.ts.map +0 -1
  13. package/dist/scripts/generate-web-module/templates/project/bootstrapper.template.js +0 -141
  14. package/dist/scripts/generate-web-module/templates/project/bootstrapper.template.js.map +0 -1
  15. package/dist/scripts/generate-web-module/templates/project/env.template.d.ts +0 -7
  16. package/dist/scripts/generate-web-module/templates/project/env.template.d.ts.map +0 -1
  17. package/dist/scripts/generate-web-module/templates/project/env.template.js +0 -110
  18. package/dist/scripts/generate-web-module/templates/project/env.template.js.map +0 -1
  19. package/dist/scripts/generate-web-module/templates/project/main-layout.template.d.ts +0 -7
  20. package/dist/scripts/generate-web-module/templates/project/main-layout.template.d.ts.map +0 -1
  21. package/dist/scripts/generate-web-module/templates/project/main-layout.template.js +0 -101
  22. package/dist/scripts/generate-web-module/templates/project/main-layout.template.js.map +0 -1
  23. package/dist/scripts/generate-web-module/templates/project/middleware-env.template.d.ts +0 -7
  24. package/dist/scripts/generate-web-module/templates/project/middleware-env.template.d.ts.map +0 -1
  25. package/dist/scripts/generate-web-module/templates/project/middleware-env.template.js +0 -66
  26. package/dist/scripts/generate-web-module/templates/project/middleware-env.template.js.map +0 -1
  27. package/dist/scripts/generate-web-module/templates/project/settings-container.template.d.ts +0 -7
  28. package/dist/scripts/generate-web-module/templates/project/settings-container.template.d.ts.map +0 -1
  29. package/dist/scripts/generate-web-module/templates/project/settings-container.template.js +0 -257
  30. package/dist/scripts/generate-web-module/templates/project/settings-container.template.js.map +0 -1
  31. package/dist/scripts/generate-web-module/templates/project/settings-context.template.d.ts +0 -7
  32. package/dist/scripts/generate-web-module/templates/project/settings-context.template.d.ts.map +0 -1
  33. package/dist/scripts/generate-web-module/templates/project/settings-context.template.js +0 -124
  34. package/dist/scripts/generate-web-module/templates/project/settings-context.template.js.map +0 -1
  35. package/dist/scripts/generate-web-module/templates/project/settings-module-page.template.d.ts +0 -7
  36. package/dist/scripts/generate-web-module/templates/project/settings-module-page.template.d.ts.map +0 -1
  37. package/dist/scripts/generate-web-module/templates/project/settings-module-page.template.js +0 -78
  38. package/dist/scripts/generate-web-module/templates/project/settings-module-page.template.js.map +0 -1
  39. package/dist/scripts/generate-web-module/templates/project/settings-page.template.d.ts +0 -7
  40. package/dist/scripts/generate-web-module/templates/project/settings-page.template.d.ts.map +0 -1
  41. package/dist/scripts/generate-web-module/templates/project/settings-page.template.js +0 -75
  42. package/dist/scripts/generate-web-module/templates/project/settings-page.template.js.map +0 -1
  43. package/scripts/generate-web-module/templates/project/bootstrapper.template.ts +0 -108
  44. package/scripts/generate-web-module/templates/project/env.template.ts +0 -77
  45. package/scripts/generate-web-module/templates/project/main-layout.template.tsx +0 -68
  46. package/scripts/generate-web-module/templates/project/middleware-env.template.ts +0 -33
  47. package/scripts/generate-web-module/templates/project/settings-container.template.tsx +0 -224
  48. package/scripts/generate-web-module/templates/project/settings-context.template.tsx +0 -91
  49. package/scripts/generate-web-module/templates/project/settings-module-page.template.tsx +0 -45
  50. package/scripts/generate-web-module/templates/project/settings-page.template.tsx +0 -42
@@ -1,224 +0,0 @@
1
- /**
2
- * Settings Container Template
3
- *
4
- * Generates SettingsContainer with company and billing sections (including Stripe)
5
- */
6
-
7
- import * as fs from "fs";
8
- import * as path from "path";
9
-
10
- export function generateSettingsContainerTemplate(webBasePath: string): string | null {
11
- const outputPath = path.join(webBasePath, "apps/web/src/features/common/components/containers/SettingsContainer.tsx");
12
-
13
- if (fs.existsSync(outputPath)) {
14
- return null; // Skip if exists
15
- }
16
-
17
- return `"use client";
18
-
19
- import { useSettingsContext } from "@/features/common/contexts/SettingsContext";
20
- import { cn } from "@/utils/cn";
21
- import { Action, ModuleWithPermissions, Modules, getRoleId } from "@carlonicora/nextjs-jsonapi";
22
- import { usePageUrlGenerator } from "@carlonicora/nextjs-jsonapi/client";
23
- import {
24
- CompanyContainer,
25
- ContentTitle,
26
- InvoicesContainer,
27
- PaymentMethodsContainer,
28
- ProductsAdminContainer,
29
- SubscriptionsContainer,
30
- UsageContainer,
31
- UsersListContainer,
32
- isStripeConfigured,
33
- } from "@carlonicora/nextjs-jsonapi/components";
34
- import { useCurrentUserContext } from "@carlonicora/nextjs-jsonapi/contexts";
35
-
36
- import { Activity, Building2Icon, CreditCard, LucideIcon, Package, Receipt, UsersIcon, Wallet } from "lucide-react";
37
- import { useLocale, useTranslations } from "next-intl";
38
- import { ReactNode, useEffect, useState } from "react";
39
-
40
- type SidebarItem = {
41
- id: string;
42
- icon: LucideIcon;
43
- label?: string;
44
- container: ReactNode;
45
- module: ModuleWithPermissions;
46
- singleItem?: boolean;
47
- requiredPermission?: Action;
48
- requiredRole?: string;
49
- hidden?: boolean;
50
- };
51
-
52
- export default function SettingsContainer() {
53
- const { module, setModule } = useSettingsContext();
54
- const t = useTranslations();
55
- const locale = useLocale();
56
- const { hasPermissionToModule, hasRole } = useCurrentUserContext();
57
- const [selectedComponent, setSelectedComponent] = useState<SidebarItem | null>(null);
58
- const [subscriptions, setSubscriptions] = useState<any[]>([]);
59
- const generateUrl = usePageUrlGenerator();
60
-
61
- // Helper function to check if company has metered subscriptions
62
- const hasMeteredSubscriptions = (): boolean => {
63
- return subscriptions.some((sub) => sub.price?.recurring?.usageType === "metered");
64
- };
65
-
66
- // Build sidebars array - only include billing if Stripe is configured
67
- const billingSection = isStripeConfigured()
68
- ? !hasRole(getRoleId().Administrator)
69
- ? {
70
- name: "billing",
71
- label: t("billing.title"),
72
- items: [
73
- {
74
- id: "billing-subscriptions",
75
- icon: CreditCard,
76
- label: t("billing.subscriptions.title"),
77
- container: <SubscriptionsContainer />,
78
- module: Modules.Billing,
79
- requiredPermission: Action.Read,
80
- },
81
- {
82
- id: "billing-payment-methods",
83
- icon: Wallet,
84
- label: t("billing.payment_methods.title"),
85
- container: <PaymentMethodsContainer />,
86
- module: Modules.Billing,
87
- requiredPermission: Action.Read,
88
- },
89
- {
90
- id: "billing-invoices",
91
- icon: Receipt,
92
- label: t("billing.invoices.title"),
93
- container: <InvoicesContainer />,
94
- module: Modules.Billing,
95
- requiredPermission: Action.Read,
96
- },
97
- {
98
- id: "billing-usage",
99
- icon: Activity,
100
- label: t("billing.usage.title"),
101
- container: <UsageContainer />,
102
- module: Modules.Billing,
103
- requiredPermission: Action.Read,
104
- hidden: !hasMeteredSubscriptions(),
105
- },
106
- ],
107
- }
108
- : {
109
- name: \`billing\`,
110
- label: t("billing.title"),
111
- items: [
112
- {
113
- id: "billing-admin-products",
114
- icon: Package,
115
- label: t("billing.admin.products.title"),
116
- container: <ProductsAdminContainer />,
117
- module: Modules.Billing,
118
- requiredRole: getRoleId().Administrator,
119
- },
120
- ],
121
- }
122
- : null;
123
-
124
- const companySection = !hasRole(getRoleId().Administrator)
125
- ? {
126
- name: \`company\`,
127
- items: [
128
- {
129
- id: "company",
130
- icon: Building2Icon,
131
- container: <CompanyContainer />,
132
- module: Modules.Company,
133
- singleItem: true,
134
- },
135
- { id: "user", icon: UsersIcon, container: <UsersListContainer />, module: Modules.User },
136
- ],
137
- }
138
- : null;
139
-
140
- const sidebars: {
141
- name: string;
142
- label?: string;
143
- items: SidebarItem[];
144
- }[] = [...(companySection ? [companySection] : []), ...(billingSection ? [billingSection] : [])];
145
-
146
- useEffect(() => {
147
- if (module) {
148
- const found = sidebars
149
- .map((sidebar) => sidebar.items.find((item) => item.module.name === module.name))
150
- .find((item) => item !== undefined);
151
- if (found) {
152
- setSelectedComponent(found);
153
- } else {
154
- setSelectedComponent(null);
155
- setModule(undefined);
156
- }
157
- } else {
158
- setSelectedComponent(null);
159
- }
160
- }, [module]);
161
-
162
- return (
163
- <div className="flex w-full gap-x-4">
164
- <div className="sticky top-16 flex h-[calc(100vh-theme(spacing.20))] w-2xl flex-col justify-between border-r pr-4">
165
- <div className="flex min-h-0 flex-1 overflow-y-auto">
166
- <div className="flex w-full flex-col">
167
- <ContentTitle element={t(\`generic.settings\`)} />
168
- <nav className="space-y-4">
169
- {sidebars.map((sidebar) => (
170
- <div key={sidebar.name} className="">
171
- <h3 className="text-muted-foreground mb-2 text-lg font-light">
172
- {sidebar.label || t(\`generic.settings_sidebar\`, { item: sidebar.name })}
173
- </h3>
174
- {sidebar.items.map((item) => {
175
- // Check if item is hidden
176
- if (item.hidden) return null;
177
-
178
- // Check permission-based access
179
- if (item.requiredPermission) {
180
- if (!hasPermissionToModule({ module: item.module, action: item.requiredPermission })) return null;
181
- } else if (!hasPermissionToModule({ module: item.module, action: Action.Read })) {
182
- return null;
183
- }
184
-
185
- // Check role-based access
186
- if (item.requiredRole && !hasRole(item.requiredRole)) return null;
187
-
188
- const Icon = item.icon;
189
- return (
190
- <button
191
- key={item.id}
192
- onClick={() => {
193
- setModule(item.module);
194
- setSelectedComponent(item);
195
- window.history.replaceState(
196
- null,
197
- "",
198
- generateUrl({ page: \`/settings\`, id: item.module.name, language: locale }),
199
- );
200
- }}
201
- className={cn(
202
- "flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors",
203
- selectedComponent?.id === item.id
204
- ? "bg-secondary text-secondary-foreground"
205
- : "text-muted-foreground hover:text-foreground hover:bg-secondary/50",
206
- )}
207
- >
208
- <Icon className="h-4 w-4" />
209
- {item.label || t(\`types.\${item.module.name}\`, { count: item.singleItem ? 1 : 2 })}
210
- </button>
211
- );
212
- })}
213
- </div>
214
- ))}
215
- </nav>
216
- </div>
217
- </div>
218
- </div>
219
- <div className="flex w-full flex-col gap-y-4 pb-20">{selectedComponent ? selectedComponent.container : null}</div>
220
- </div>
221
- );
222
- }
223
- `;
224
- }
@@ -1,91 +0,0 @@
1
- /**
2
- * Settings Context Template
3
- *
4
- * Generates SettingsContext with module-based navigation
5
- */
6
-
7
- import * as fs from "fs";
8
- import * as path from "path";
9
-
10
- export function generateSettingsContextTemplate(webBasePath: string): string | null {
11
- const outputPath = path.join(webBasePath, "apps/web/src/features/common/contexts/SettingsContext.tsx");
12
-
13
- if (fs.existsSync(outputPath)) {
14
- return null; // Skip if exists
15
- }
16
-
17
- return `"use client";
18
-
19
- import { BreadcrumbItemData, Modules, ModuleWithPermissions } from "@carlonicora/nextjs-jsonapi";
20
- import { usePageUrlGenerator } from "@carlonicora/nextjs-jsonapi/client";
21
- import { SharedProvider } from "@carlonicora/nextjs-jsonapi/contexts";
22
- import { useTranslations } from "next-intl";
23
- import { createContext, ReactNode, useContext, useState } from "react";
24
-
25
- interface SettingsContextType {
26
- module?: ModuleWithPermissions;
27
- setModule: (module: ModuleWithPermissions | undefined) => void;
28
- }
29
-
30
- const SettingsContext = createContext<SettingsContextType | undefined>(undefined);
31
-
32
- type SettingsProviderProps = {
33
- children: ReactNode;
34
- moduleName?: string;
35
- };
36
-
37
- export const SettingsProvider = ({ children, moduleName }: SettingsProviderProps) => {
38
- const [module, setModule] = useState<ModuleWithPermissions | undefined>(
39
- moduleName ? Modules.findByName(moduleName) : undefined,
40
- );
41
- const generateUrl = usePageUrlGenerator();
42
- const t = useTranslations();
43
-
44
- const breadcrumb = () => {
45
- const response: BreadcrumbItemData[] = [];
46
-
47
- response.push({
48
- name: t(\`generic.settings\`),
49
- href: generateUrl({ page: \`/settings\` }),
50
- });
51
-
52
- if (module)
53
- response.push({
54
- name: t(\`types.\${module.name}\`, { count: 2 }),
55
- href: generateUrl({ page: \`/settings\`, id: module.name }),
56
- });
57
-
58
- return response;
59
- };
60
-
61
- const title = () => {
62
- const response: any = {
63
- type: t(\`generic.settings\`),
64
- };
65
-
66
- return response;
67
- };
68
-
69
- return (
70
- <SharedProvider value={{ breadcrumbs: breadcrumb(), title: title() }}>
71
- <SettingsContext.Provider
72
- value={{
73
- module: module,
74
- setModule: setModule,
75
- }}
76
- >
77
- {children}
78
- </SettingsContext.Provider>
79
- </SharedProvider>
80
- );
81
- };
82
-
83
- export const useSettingsContext = (): SettingsContextType => {
84
- const context = useContext(SettingsContext);
85
- if (context === undefined) {
86
- throw new Error("useSettingsContext must be used within a SettingsProvider");
87
- }
88
- return context;
89
- };
90
- `;
91
- }
@@ -1,45 +0,0 @@
1
- /**
2
- * Settings Module Page Template
3
- *
4
- * Generates module-specific settings page with module selection
5
- */
6
-
7
- import * as fs from "fs";
8
- import * as path from "path";
9
-
10
- export function generateSettingsModulePageTemplate(webBasePath: string): string | null {
11
- const outputPath = path.join(webBasePath, "apps/web/src/app/[locale]/(main)/(features)/settings/[module]/page.tsx");
12
-
13
- if (fs.existsSync(outputPath)) {
14
- return null; // Skip if exists
15
- }
16
-
17
- return `import SettingsContainer from "@/features/common/components/containers/SettingsContainer";
18
- import { SettingsProvider } from "@/features/common/contexts/SettingsContext";
19
- import { generateSpecificMetadata } from "@/utils/metadata";
20
- import { PageContainer } from "@carlonicora/nextjs-jsonapi/components";
21
- import { Metadata } from "next";
22
- import { getTranslations } from "next-intl/server";
23
-
24
- export async function generateMetadata(): Promise<Metadata> {
25
- const t = await getTranslations();
26
-
27
- const title = t(\`generic.settings\`);
28
-
29
- return await generateSpecificMetadata({ title: title });
30
- }
31
-
32
- export default async function SettingsModulePage(props: {
33
- params: Promise<{ module: string }>;
34
- }) {
35
- const params = await props.params;
36
- return (
37
- <SettingsProvider moduleName={params.module}>
38
- <PageContainer>
39
- <SettingsContainer />
40
- </PageContainer>
41
- </SettingsProvider>
42
- );
43
- }
44
- `;
45
- }
@@ -1,42 +0,0 @@
1
- /**
2
- * Settings Page Template
3
- *
4
- * Generates base settings page without module selection
5
- */
6
-
7
- import * as fs from "fs";
8
- import * as path from "path";
9
-
10
- export function generateSettingsPageTemplate(webBasePath: string): string | null {
11
- const outputPath = path.join(webBasePath, "apps/web/src/app/[locale]/(main)/(features)/settings/page.tsx");
12
-
13
- if (fs.existsSync(outputPath)) {
14
- return null; // Skip if exists
15
- }
16
-
17
- return `import SettingsContainer from "@/features/common/components/containers/SettingsContainer";
18
- import { SettingsProvider } from "@/features/common/contexts/SettingsContext";
19
- import { generateSpecificMetadata } from "@/utils/metadata";
20
- import { PageContainer } from "@carlonicora/nextjs-jsonapi/components";
21
- import { Metadata } from "next";
22
- import { getTranslations } from "next-intl/server";
23
-
24
- export async function generateMetadata(): Promise<Metadata> {
25
- const t = await getTranslations();
26
-
27
- const title = t(\`generic.settings\`);
28
-
29
- return await generateSpecificMetadata({ title: title });
30
- }
31
-
32
- export default async function SettingsPage() {
33
- return (
34
- <SettingsProvider>
35
- <PageContainer>
36
- <SettingsContainer />
37
- </PageContainer>
38
- </SettingsProvider>
39
- );
40
- }
41
- `;
42
- }