@carlonicora/nextjs-jsonapi 1.74.0 → 1.76.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.
Files changed (32) hide show
  1. package/dist/{BlockNoteEditor-KJZ7FGBA.mjs → BlockNoteEditor-MB6LMBQQ.mjs} +2 -2
  2. package/dist/{BlockNoteEditor-A37P3FA7.js → BlockNoteEditor-SSFD4U5L.js} +6 -6
  3. package/dist/{BlockNoteEditor-A37P3FA7.js.map → BlockNoteEditor-SSFD4U5L.js.map} +1 -1
  4. package/dist/billing/index.js +299 -299
  5. package/dist/billing/index.mjs +1 -1
  6. package/dist/{chunk-XUTMY6K5.js → chunk-4C5ZDJV6.js} +844 -696
  7. package/dist/chunk-4C5ZDJV6.js.map +1 -0
  8. package/dist/{chunk-ZNODEBMI.mjs → chunk-JLZBOSTY.mjs} +2971 -2823
  9. package/dist/chunk-JLZBOSTY.mjs.map +1 -0
  10. package/dist/client/index.js +2 -2
  11. package/dist/client/index.mjs +1 -1
  12. package/dist/components/index.d.mts +37 -3
  13. package/dist/components/index.d.ts +37 -3
  14. package/dist/components/index.js +6 -2
  15. package/dist/components/index.js.map +1 -1
  16. package/dist/components/index.mjs +5 -1
  17. package/dist/contexts/index.js +2 -2
  18. package/dist/contexts/index.mjs +1 -1
  19. package/dist/scripts/generate-web-module/templates/components/multi-selector.template.d.ts.map +1 -1
  20. package/dist/scripts/generate-web-module/templates/components/multi-selector.template.js +14 -120
  21. package/dist/scripts/generate-web-module/templates/components/multi-selector.template.js.map +1 -1
  22. package/package.json +1 -1
  23. package/scripts/generate-web-module/templates/components/multi-selector.template.ts +14 -120
  24. package/src/components/forms/EditorSheet.tsx +6 -2
  25. package/src/components/forms/EntityMultiSelector.tsx +325 -0
  26. package/src/components/forms/index.ts +1 -0
  27. package/src/features/how-to/components/forms/HowToMultiSelector.tsx +14 -120
  28. package/src/features/user/components/forms/UserMultiSelect.tsx +34 -181
  29. package/src/features/user/components/widgets/index.ts +1 -0
  30. package/dist/chunk-XUTMY6K5.js.map +0 -1
  31. package/dist/chunk-ZNODEBMI.mjs.map +0 -1
  32. /package/dist/{BlockNoteEditor-KJZ7FGBA.mjs.map → BlockNoteEditor-MB6LMBQQ.mjs.map} +0 -0
@@ -21,18 +21,9 @@ function generateMultiSelectorTemplate(data) {
21
21
 
22
22
  import { ${names.pascalCase}Interface } from "@/features/${data.importTargetDir}/${names.kebabCase}/data/${names.pascalCase}Interface";
23
23
  import { ${names.pascalCase}Service } from "@/features/${data.importTargetDir}/${names.kebabCase}/data/${names.pascalCase}Service";
24
- import { DataListRetriever, useDataListRetriever, useDebounce } from "@carlonicora/nextjs-jsonapi/client";
25
- import { FormFieldWrapper, MultipleSelector } from "@carlonicora/nextjs-jsonapi/components";
26
- import { Option } from "@carlonicora/nextjs-jsonapi/components";
24
+ import { EntityMultiSelector } from "@carlonicora/nextjs-jsonapi/components";
27
25
  import { Modules } from "@carlonicora/nextjs-jsonapi/core";
28
- import { useCallback, useEffect, useMemo, useState } from "react";
29
26
  import { useTranslations } from "next-intl";
30
- import { useWatch } from "react-hook-form";
31
-
32
- type ${names.pascalCase}MultiSelectType = {
33
- id: string;
34
- ${displayProp}: string;
35
- };
36
27
 
37
28
  type ${names.pascalCase}MultiSelectorProps = {
38
29
  id: string;
@@ -45,10 +36,6 @@ type ${names.pascalCase}MultiSelectorProps = {
45
36
  isRequired?: boolean;
46
37
  };
47
38
 
48
- type ${names.pascalCase}Option = Option & {
49
- ${names.camelCase}Data?: ${names.pascalCase}Interface;
50
- };
51
-
52
39
  export default function ${names.pascalCase}MultiSelector({
53
40
  id,
54
41
  form,
@@ -56,117 +43,24 @@ export default function ${names.pascalCase}MultiSelector({
56
43
  label,
57
44
  placeholder,
58
45
  onChange,
59
- maxCount = 3,
60
46
  isRequired = false,
61
47
  }: ${names.pascalCase}MultiSelectorProps) {
62
48
  const t = useTranslations();
63
- const [${names.camelCase}Options, set${names.pascalCase}Options] = useState<${names.pascalCase}Option[]>([]);
64
- const [searchTerm, setSearchTerm] = useState<string>("");
65
-
66
- const selected${names.pluralPascal}: ${names.pascalCase}MultiSelectType[] = useWatch({ control: form.control, name: id }) || [];
67
-
68
- const data: DataListRetriever<${names.pascalCase}Interface> = useDataListRetriever({
69
- retriever: (params) => ${names.pascalCase}Service.findMany(params),
70
- retrieverParams: {},
71
- ready: true,
72
- module: Modules.${names.pascalCase},
73
- });
74
-
75
- const updateSearch = useCallback(
76
- (searchedTerm: string) => {
77
- if (searchedTerm.trim()) {
78
- data.addAdditionalParameter("search", searchedTerm.trim());
79
- } else {
80
- data.removeAdditionalParameter("search");
81
- }
82
- },
83
- [data]
84
- );
85
-
86
- const debouncedUpdateSearch = useDebounce(updateSearch, 500);
87
-
88
- useEffect(() => {
89
- debouncedUpdateSearch(searchTerm);
90
- }, [debouncedUpdateSearch, searchTerm]);
91
-
92
- useEffect(() => {
93
- if (data.data && data.data.length > 0) {
94
- const ${names.pluralCamel} = data.data as ${names.pascalCase}Interface[];
95
- const filtered${names.pluralPascal} = ${names.pluralCamel}.filter((${names.camelCase}) => ${names.camelCase}.id !== current${names.pascalCase}?.id);
96
-
97
- const options: ${names.pascalCase}Option[] = filtered${names.pluralPascal}.map((${names.camelCase}) => ({
98
- label: ${names.camelCase}.${displayProp},
99
- value: ${names.camelCase}.id,
100
- ${names.camelCase}Data: ${names.camelCase},
101
- }));
102
-
103
- // Add options for any already selected that aren't in search results
104
- const existingOptionIds = new Set(options.map((option) => option.value));
105
- const missingOptions: ${names.pascalCase}Option[] = selected${names.pluralPascal}
106
- .filter((${names.camelCase}) => !existingOptionIds.has(${names.camelCase}.id))
107
- .map((${names.camelCase}) => ({
108
- label: ${names.camelCase}.${displayProp},
109
- value: ${names.camelCase}.id,
110
- ${names.camelCase}Data: ${names.camelCase} as unknown as ${names.pascalCase}Interface,
111
- }));
112
-
113
- set${names.pascalCase}Options([...options, ...missingOptions]);
114
- }
115
- }, [data.data, current${names.pascalCase}, selected${names.pluralPascal}]);
116
-
117
- // Convert selected to Option[] format
118
- const selectedOptions = useMemo(() => {
119
- return selected${names.pluralPascal}.map((${names.camelCase}) => ({
120
- value: ${names.camelCase}.id,
121
- label: ${names.camelCase}.${displayProp},
122
- }));
123
- }, [selected${names.pluralPascal}]);
124
-
125
- const handleChange = (options: Option[]) => {
126
- // Convert to form format
127
- const formValues = options.map((option) => ({
128
- id: option.value,
129
- ${displayProp}: option.label,
130
- }));
131
-
132
- form.setValue(id, formValues, { shouldDirty: true, shouldTouch: true });
133
-
134
- if (onChange) {
135
- // Get full data for onChange callback
136
- const fullData = options
137
- .map((option) => {
138
- const ${names.camelCase}Option = ${names.camelCase}Options.find((opt) => opt.value === option.value);
139
- return ${names.camelCase}Option?.${names.camelCase}Data;
140
- })
141
- .filter(Boolean) as ${names.pascalCase}Interface[];
142
- onChange(fullData);
143
- }
144
- };
145
-
146
- // Search handler
147
- const handleSearchSync = (search: string): Option[] => {
148
- setSearchTerm(search);
149
- return ${names.camelCase}Options;
150
- };
151
49
 
152
50
  return (
153
- <div className="flex w-full flex-col">
154
- <FormFieldWrapper form={form} name={id} label={label} isRequired={isRequired}>
155
- {() => (
156
- <MultipleSelector
157
- value={selectedOptions}
158
- onChange={handleChange}
159
- options={${names.camelCase}Options}
160
- placeholder={placeholder}
161
- maxDisplayCount={maxCount}
162
- hideClearAllButton
163
- onSearchSync={handleSearchSync}
164
- delay={0}
165
- emptyIndicator={<span className="text-muted-foreground">{t("ui.search.no_results_generic")}</span>}
166
- />
167
- )}
168
- </FormFieldWrapper>
169
- </div>
51
+ <EntityMultiSelector<${names.pascalCase}Interface>
52
+ id={id}
53
+ form={form}
54
+ label={label}
55
+ placeholder={placeholder || t("ui.search.button")}
56
+ emptyText={t("ui.search.no_results_generic")}
57
+ isRequired={isRequired}
58
+ retriever={(params) => ${names.pascalCase}Service.findMany(params)}
59
+ module={Modules.${names.pascalCase}}
60
+ getLabel={(${names.camelCase}) => ${displayProp === "id" ? `${names.camelCase}.id` : `${names.camelCase}.${displayProp}`}}
61
+ excludeId={current${names.pascalCase}?.id}
62
+ onChange={onChange}
63
+ />
170
64
  );
171
65
  }
172
66
  `;
@@ -1 +1 @@
1
- {"version":3,"file":"multi-selector.template.js","sourceRoot":"","sources":["../../../../../scripts/generate-web-module/templates/components/multi-selector.template.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"multi-selector.template.js","sourceRoot":"","sources":["../../../../../scripts/generate-web-module/templates/components/multi-selector.template.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAUH,sEAqDC;AA3DD;;;;;GAKG;AACH,SAAgB,6BAA6B,CAAC,IAA0B;IACtE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAC/C,MAAM,YAAY,GAAG,cAAc,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IAC7E,MAAM,gBAAgB,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,KAAK,eAAe,CAAC,CAAC;IACnG,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,gBAAgB,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC;IAE7E,OAAO;;WAEE,KAAK,CAAC,UAAU,gCAAgC,IAAI,CAAC,eAAe,IAAI,KAAK,CAAC,SAAS,SAAS,KAAK,CAAC,UAAU;WAChH,KAAK,CAAC,UAAU,8BAA8B,IAAI,CAAC,eAAe,IAAI,KAAK,CAAC,SAAS,SAAS,KAAK,CAAC,UAAU;;;;;OAKlH,KAAK,CAAC,UAAU;;;WAGZ,KAAK,CAAC,UAAU,MAAM,KAAK,CAAC,UAAU;;;gBAGjC,KAAK,CAAC,WAAW,MAAM,KAAK,CAAC,UAAU;;;;;0BAK7B,KAAK,CAAC,UAAU;;;WAG/B,KAAK,CAAC,UAAU;;;;;KAKtB,KAAK,CAAC,UAAU;;;;2BAIM,KAAK,CAAC,UAAU;;;;;;;+BAOZ,KAAK,CAAC,UAAU;wBACvB,KAAK,CAAC,UAAU;mBACrB,KAAK,CAAC,SAAS,QAAQ,WAAW,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,SAAS,IAAI,WAAW,EAAE;0BACpG,KAAK,CAAC,UAAU;;;;;CAKzC,CAAC;AACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carlonicora/nextjs-jsonapi",
3
- "version": "1.74.0",
3
+ "version": "1.76.0",
4
4
  "description": "Next.js JSON:API client with server/client support and caching",
5
5
  "author": "Carlo Nicora",
6
6
  "license": "GPL-3.0-or-later",
@@ -22,18 +22,9 @@ export function generateMultiSelectorTemplate(data: FrontendTemplateData): strin
22
22
 
23
23
  import { ${names.pascalCase}Interface } from "@/features/${data.importTargetDir}/${names.kebabCase}/data/${names.pascalCase}Interface";
24
24
  import { ${names.pascalCase}Service } from "@/features/${data.importTargetDir}/${names.kebabCase}/data/${names.pascalCase}Service";
25
- import { DataListRetriever, useDataListRetriever, useDebounce } from "@carlonicora/nextjs-jsonapi/client";
26
- import { FormFieldWrapper, MultipleSelector } from "@carlonicora/nextjs-jsonapi/components";
27
- import { Option } from "@carlonicora/nextjs-jsonapi/components";
25
+ import { EntityMultiSelector } from "@carlonicora/nextjs-jsonapi/components";
28
26
  import { Modules } from "@carlonicora/nextjs-jsonapi/core";
29
- import { useCallback, useEffect, useMemo, useState } from "react";
30
27
  import { useTranslations } from "next-intl";
31
- import { useWatch } from "react-hook-form";
32
-
33
- type ${names.pascalCase}MultiSelectType = {
34
- id: string;
35
- ${displayProp}: string;
36
- };
37
28
 
38
29
  type ${names.pascalCase}MultiSelectorProps = {
39
30
  id: string;
@@ -46,10 +37,6 @@ type ${names.pascalCase}MultiSelectorProps = {
46
37
  isRequired?: boolean;
47
38
  };
48
39
 
49
- type ${names.pascalCase}Option = Option & {
50
- ${names.camelCase}Data?: ${names.pascalCase}Interface;
51
- };
52
-
53
40
  export default function ${names.pascalCase}MultiSelector({
54
41
  id,
55
42
  form,
@@ -57,117 +44,24 @@ export default function ${names.pascalCase}MultiSelector({
57
44
  label,
58
45
  placeholder,
59
46
  onChange,
60
- maxCount = 3,
61
47
  isRequired = false,
62
48
  }: ${names.pascalCase}MultiSelectorProps) {
63
49
  const t = useTranslations();
64
- const [${names.camelCase}Options, set${names.pascalCase}Options] = useState<${names.pascalCase}Option[]>([]);
65
- const [searchTerm, setSearchTerm] = useState<string>("");
66
-
67
- const selected${names.pluralPascal}: ${names.pascalCase}MultiSelectType[] = useWatch({ control: form.control, name: id }) || [];
68
-
69
- const data: DataListRetriever<${names.pascalCase}Interface> = useDataListRetriever({
70
- retriever: (params) => ${names.pascalCase}Service.findMany(params),
71
- retrieverParams: {},
72
- ready: true,
73
- module: Modules.${names.pascalCase},
74
- });
75
-
76
- const updateSearch = useCallback(
77
- (searchedTerm: string) => {
78
- if (searchedTerm.trim()) {
79
- data.addAdditionalParameter("search", searchedTerm.trim());
80
- } else {
81
- data.removeAdditionalParameter("search");
82
- }
83
- },
84
- [data]
85
- );
86
-
87
- const debouncedUpdateSearch = useDebounce(updateSearch, 500);
88
-
89
- useEffect(() => {
90
- debouncedUpdateSearch(searchTerm);
91
- }, [debouncedUpdateSearch, searchTerm]);
92
-
93
- useEffect(() => {
94
- if (data.data && data.data.length > 0) {
95
- const ${names.pluralCamel} = data.data as ${names.pascalCase}Interface[];
96
- const filtered${names.pluralPascal} = ${names.pluralCamel}.filter((${names.camelCase}) => ${names.camelCase}.id !== current${names.pascalCase}?.id);
97
-
98
- const options: ${names.pascalCase}Option[] = filtered${names.pluralPascal}.map((${names.camelCase}) => ({
99
- label: ${names.camelCase}.${displayProp},
100
- value: ${names.camelCase}.id,
101
- ${names.camelCase}Data: ${names.camelCase},
102
- }));
103
-
104
- // Add options for any already selected that aren't in search results
105
- const existingOptionIds = new Set(options.map((option) => option.value));
106
- const missingOptions: ${names.pascalCase}Option[] = selected${names.pluralPascal}
107
- .filter((${names.camelCase}) => !existingOptionIds.has(${names.camelCase}.id))
108
- .map((${names.camelCase}) => ({
109
- label: ${names.camelCase}.${displayProp},
110
- value: ${names.camelCase}.id,
111
- ${names.camelCase}Data: ${names.camelCase} as unknown as ${names.pascalCase}Interface,
112
- }));
113
-
114
- set${names.pascalCase}Options([...options, ...missingOptions]);
115
- }
116
- }, [data.data, current${names.pascalCase}, selected${names.pluralPascal}]);
117
-
118
- // Convert selected to Option[] format
119
- const selectedOptions = useMemo(() => {
120
- return selected${names.pluralPascal}.map((${names.camelCase}) => ({
121
- value: ${names.camelCase}.id,
122
- label: ${names.camelCase}.${displayProp},
123
- }));
124
- }, [selected${names.pluralPascal}]);
125
-
126
- const handleChange = (options: Option[]) => {
127
- // Convert to form format
128
- const formValues = options.map((option) => ({
129
- id: option.value,
130
- ${displayProp}: option.label,
131
- }));
132
-
133
- form.setValue(id, formValues, { shouldDirty: true, shouldTouch: true });
134
-
135
- if (onChange) {
136
- // Get full data for onChange callback
137
- const fullData = options
138
- .map((option) => {
139
- const ${names.camelCase}Option = ${names.camelCase}Options.find((opt) => opt.value === option.value);
140
- return ${names.camelCase}Option?.${names.camelCase}Data;
141
- })
142
- .filter(Boolean) as ${names.pascalCase}Interface[];
143
- onChange(fullData);
144
- }
145
- };
146
-
147
- // Search handler
148
- const handleSearchSync = (search: string): Option[] => {
149
- setSearchTerm(search);
150
- return ${names.camelCase}Options;
151
- };
152
50
 
153
51
  return (
154
- <div className="flex w-full flex-col">
155
- <FormFieldWrapper form={form} name={id} label={label} isRequired={isRequired}>
156
- {() => (
157
- <MultipleSelector
158
- value={selectedOptions}
159
- onChange={handleChange}
160
- options={${names.camelCase}Options}
161
- placeholder={placeholder}
162
- maxDisplayCount={maxCount}
163
- hideClearAllButton
164
- onSearchSync={handleSearchSync}
165
- delay={0}
166
- emptyIndicator={<span className="text-muted-foreground">{t("ui.search.no_results_generic")}</span>}
167
- />
168
- )}
169
- </FormFieldWrapper>
170
- </div>
52
+ <EntityMultiSelector<${names.pascalCase}Interface>
53
+ id={id}
54
+ form={form}
55
+ label={label}
56
+ placeholder={placeholder || t("ui.search.button")}
57
+ emptyText={t("ui.search.no_results_generic")}
58
+ isRequired={isRequired}
59
+ retriever={(params) => ${names.pascalCase}Service.findMany(params)}
60
+ module={Modules.${names.pascalCase}}
61
+ getLabel={(${names.camelCase}) => ${displayProp === "id" ? `${names.camelCase}.id` : `${names.camelCase}.${displayProp}`}}
62
+ excludeId={current${names.pascalCase}?.id}
63
+ onChange={onChange}
64
+ />
171
65
  );
172
66
  }
173
67
  `;
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useTranslations } from "next-intl";
4
- import { ReactNode, useCallback, useEffect } from "react";
4
+ import { ReactNode, useCallback, useEffect, useRef } from "react";
5
5
  import { FieldValues, UseFormReturn } from "react-hook-form";
6
6
  import { PencilIcon } from "lucide-react";
7
7
  import { ModuleWithPermissions } from "../../permissions/types";
@@ -97,8 +97,12 @@ export function EditorSheet<T extends FieldValues>({
97
97
  { dialogOpen, onDialogOpenChange, forceShow },
98
98
  );
99
99
 
100
+ const hasBeenOpen = useRef(false);
101
+
100
102
  useEffect(() => {
101
- if (!open) {
103
+ if (open) {
104
+ hasBeenOpen.current = true;
105
+ } else if (hasBeenOpen.current) {
102
106
  form.reset(onReset());
103
107
  onClose?.();
104
108
  }