@carlonicora/nextjs-jsonapi 6.29.0 → 6.30.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 (36) hide show
  1. package/dist/{BlockNoteEditor-KGCHQQVY.js → BlockNoteEditor-FTRTY4YJ.js} +15 -15
  2. package/dist/{BlockNoteEditor-KGCHQQVY.js.map → BlockNoteEditor-FTRTY4YJ.js.map} +1 -1
  3. package/dist/{BlockNoteEditor-BHCNJ2JA.mjs → BlockNoteEditor-SQQF7OCY.mjs} +2 -2
  4. package/dist/billing/index.js +378 -378
  5. package/dist/billing/index.mjs +1 -1
  6. package/dist/{chunk-UQFDOZVF.mjs → chunk-MNN6MTEN.mjs} +552 -546
  7. package/dist/chunk-MNN6MTEN.mjs.map +1 -0
  8. package/dist/{chunk-WXTEAATG.js → chunk-S4FSGMKX.js} +11 -5
  9. package/dist/chunk-S4FSGMKX.js.map +1 -0
  10. package/dist/client/index.js +2 -2
  11. package/dist/client/index.mjs +1 -1
  12. package/dist/components/forms/CommonEditorButtons.d.ts.map +1 -1
  13. package/dist/components/forms/EditorSheet.d.ts.map +1 -1
  14. package/dist/components/index.js +2 -2
  15. package/dist/components/index.mjs +1 -1
  16. package/dist/contexts/index.js +2 -2
  17. package/dist/contexts/index.mjs +1 -1
  18. package/dist/email-automations/index.js +78 -78
  19. package/dist/email-automations/index.mjs +1 -1
  20. package/dist/features/analytics/index.js +107 -107
  21. package/dist/features/analytics/index.mjs +1 -1
  22. package/dist/features/blog/index.js +93 -93
  23. package/dist/features/blog/index.mjs +1 -1
  24. package/dist/features/help/index.js +31 -31
  25. package/dist/features/help/index.mjs +1 -1
  26. package/dist/features/tokenusage/index.js +102 -102
  27. package/dist/features/tokenusage/index.mjs +1 -1
  28. package/messages/en.json +1 -0
  29. package/messages/it.json +1 -0
  30. package/package.json +1 -1
  31. package/src/components/forms/CommonEditorButtons.tsx +16 -2
  32. package/src/components/forms/EditorSheet.tsx +7 -2
  33. package/src/components/forms/__tests__/EditorSheet.spec.tsx +36 -0
  34. package/dist/chunk-UQFDOZVF.mjs.map +0 -1
  35. package/dist/chunk-WXTEAATG.js.map +0 -1
  36. /package/dist/{BlockNoteEditor-BHCNJ2JA.mjs.map → BlockNoteEditor-SQQF7OCY.mjs.map} +0 -0
@@ -1,5 +1,6 @@
1
1
  import { ReactNode } from "react";
2
2
  import { useTranslations } from "next-intl";
3
+ import { Loader2 } from "lucide-react";
3
4
  import { Button } from "../../shadcnui";
4
5
 
5
6
  type CommonEditorButtonsProps = {
@@ -22,6 +23,19 @@ export function CommonEditorButtons({
22
23
  }: CommonEditorButtonsProps) {
23
24
  const t = useTranslations();
24
25
 
26
+ const submitLabel: ReactNode = form.formState.isSubmitting ? (
27
+ <>
28
+ <Loader2 className="h-4 w-4 animate-spin" />
29
+ {t(`ui.buttons.saving`)}
30
+ </>
31
+ ) : buttonTitle ? (
32
+ buttonTitle
33
+ ) : isEdit ? (
34
+ t(`ui.buttons.confirm_update`)
35
+ ) : (
36
+ t(`ui.buttons.confirm_create`)
37
+ );
38
+
25
39
  if (centerButtons) {
26
40
  return (
27
41
  <div className="flex w-full items-center justify-between gap-x-2">
@@ -33,7 +47,7 @@ export function CommonEditorButtons({
33
47
 
34
48
  {!hideSubmit ? (
35
49
  <Button type="submit" disabled={form.formState.isSubmitting || disabled} data-testid={`modal-button-create`}>
36
- {buttonTitle ? buttonTitle : isEdit ? t(`ui.buttons.confirm_update`) : t(`ui.buttons.confirm_create`)}
50
+ {submitLabel}
37
51
  </Button>
38
52
  ) : (
39
53
  <div />
@@ -56,7 +70,7 @@ export function CommonEditorButtons({
56
70
 
57
71
  {!hideSubmit && (
58
72
  <Button type="submit" disabled={form.formState.isSubmitting || disabled} data-testid={`modal-button-create`}>
59
- {buttonTitle ? buttonTitle : isEdit ? t(`ui.buttons.confirm_update`) : t(`ui.buttons.confirm_create`)}
73
+ {submitLabel}
60
74
  </Button>
61
75
  )}
62
76
  </div>
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { useTranslations } from "next-intl";
4
- import { isValidElement, ReactElement, ReactNode, useCallback, useEffect, useRef } from "react";
4
+ import { isValidElement, ReactElement, ReactNode, useCallback, useEffect, useRef, useState } from "react";
5
5
  import { FieldValues, UseFormReturn } from "react-hook-form";
6
6
  import { PencilIcon } from "lucide-react";
7
7
  import { ActionBarProvider, useIsInActionBar } from "../../contexts/ActionBarContext";
@@ -200,7 +200,12 @@ export function EditorSheet<T extends FieldValues>({
200
200
  const hasBeenOpen = useRef(false);
201
201
  // Fingerprint of the values the form was last seeded with, used to detect that
202
202
  // the entity behind `onReset` has changed while the sheet was closed.
203
- const seeded = useRef<string | undefined>(undefined);
203
+ // An edit form starts seeded with the entity as it was at mount (the values its
204
+ // `useForm` defaults came from), so the FIRST open also notices an entity that
205
+ // changed since — e.g. content autosaved on the page — instead of showing the
206
+ // mount-time values and saving them back over the newer ones.
207
+ const [mountFingerprint] = useState(() => (isEdit ? JSON.stringify(onReset()) : undefined));
208
+ const seeded = useRef<string | undefined>(mountFingerprint);
204
209
 
205
210
  useEffect(() => {
206
211
  if (open) {
@@ -344,6 +344,42 @@ describe("EditorSheet", () => {
344
344
  });
345
345
  });
346
346
 
347
+ describe("edit form seeding", () => {
348
+ function EntityEditor({ name }: { name: string }) {
349
+ const form = useForm<TestValues>({ resolver: zodResolver(schema), defaultValues: { name } });
350
+ return (
351
+ <EditorSheet
352
+ form={form}
353
+ onSubmit={vi.fn().mockResolvedValue({ id: "123" })}
354
+ onReset={() => ({ name })}
355
+ entityType="Test Entity"
356
+ isEdit={true}
357
+ module={testModule}
358
+ >
359
+ <input {...form.register("name")} data-testid="name-input" />
360
+ </EditorSheet>
361
+ );
362
+ }
363
+
364
+ it("opens the first time with the entity as it is now, not as it was at mount", async () => {
365
+ const r = render(<EntityEditor name="Old" />);
366
+ // The page saved a newer value (e.g. autosaved content) before the sheet ever opened.
367
+ r.rerender(<EntityEditor name="New" />);
368
+
369
+ await userEvent.click(document.querySelector('[data-help="editor.edit"]') as HTMLElement);
370
+
371
+ expect(screen.getByTestId("name-input")).toHaveValue("New");
372
+ });
373
+
374
+ it("keeps the mount values on the first open when the entity did not change", async () => {
375
+ render(<EntityEditor name="Same" />);
376
+
377
+ await userEvent.click(document.querySelector('[data-help="editor.edit"]') as HTMLElement);
378
+
379
+ expect(screen.getByTestId("name-input")).toHaveValue("Same");
380
+ });
381
+ });
382
+
347
383
  describe("Activity hide and reveal (back/forward)", () => {
348
384
  it("uncontrolled sheet is closed and the form reset after Activity reveal", async () => {
349
385
  const page = (mode: "visible" | "hidden") => (