@btst/stack 1.1.4 → 1.1.5

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 (38) hide show
  1. package/dist/node_modules/.pnpm/@radix-ui_react-alert-dialog@1.1.15_@types_react-dom@19.2.2_@types_react@19.2.2__@types_ec789942cd38340bd7362c09e7d34384/node_modules/@radix-ui/react-alert-dialog/dist/index.cjs +182 -0
  2. package/dist/node_modules/.pnpm/@radix-ui_react-alert-dialog@1.1.15_@types_react-dom@19.2.2_@types_react@19.2.2__@types_ec789942cd38340bd7362c09e7d34384/node_modules/@radix-ui/react-alert-dialog/dist/index.mjs +149 -0
  3. package/dist/packages/better-stack/src/plugins/blog/client/components/forms/post-forms.cjs +64 -14
  4. package/dist/packages/better-stack/src/plugins/blog/client/components/forms/post-forms.mjs +66 -16
  5. package/dist/packages/better-stack/src/plugins/blog/client/components/pages/edit-post-page.internal.cjs +5 -1
  6. package/dist/packages/better-stack/src/plugins/blog/client/components/pages/edit-post-page.internal.mjs +5 -1
  7. package/dist/packages/better-stack/src/plugins/blog/client/hooks/blog-hooks.cjs +31 -0
  8. package/dist/packages/better-stack/src/plugins/blog/client/hooks/blog-hooks.mjs +31 -1
  9. package/dist/packages/better-stack/src/plugins/blog/client/localization/blog-forms.cjs +8 -0
  10. package/dist/packages/better-stack/src/plugins/blog/client/localization/blog-forms.mjs +8 -0
  11. package/dist/packages/ui/src/components/alert-dialog.cjs +149 -0
  12. package/dist/packages/ui/src/components/alert-dialog.mjs +137 -0
  13. package/dist/plugins/blog/api/index.d.cts +1 -1
  14. package/dist/plugins/blog/api/index.d.mts +1 -1
  15. package/dist/plugins/blog/api/index.d.ts +1 -1
  16. package/dist/plugins/blog/client/hooks/index.cjs +1 -0
  17. package/dist/plugins/blog/client/hooks/index.d.cts +10 -4
  18. package/dist/plugins/blog/client/hooks/index.d.mts +10 -4
  19. package/dist/plugins/blog/client/hooks/index.d.ts +10 -4
  20. package/dist/plugins/blog/client/hooks/index.mjs +1 -1
  21. package/dist/plugins/blog/client/index.d.cts +172 -178
  22. package/dist/plugins/blog/client/index.d.mts +172 -178
  23. package/dist/plugins/blog/client/index.d.ts +172 -178
  24. package/dist/plugins/blog/query-keys.d.cts +5 -5
  25. package/dist/plugins/blog/query-keys.d.mts +5 -5
  26. package/dist/plugins/blog/query-keys.d.ts +5 -5
  27. package/dist/plugins/client/index.d.cts +3 -2
  28. package/dist/plugins/client/index.d.mts +3 -2
  29. package/dist/plugins/client/index.d.ts +3 -2
  30. package/package.json +1 -1
  31. package/src/plugins/blog/client/components/forms/post-forms.tsx +92 -14
  32. package/src/plugins/blog/client/components/pages/edit-post-page.internal.tsx +6 -0
  33. package/src/plugins/blog/client/hooks/blog-hooks.tsx +38 -0
  34. package/src/plugins/blog/client/localization/blog-forms.ts +10 -0
  35. package/src/plugins/client/index.ts +7 -3
  36. package/dist/shared/{stack.CbuN2zVV.d.cts → stack.CoPoHVfV.d.cts} +2 -2
  37. package/dist/shared/{stack.CbuN2zVV.d.mts → stack.CoPoHVfV.d.mts} +2 -2
  38. package/dist/shared/{stack.CbuN2zVV.d.ts → stack.CoPoHVfV.d.ts} +2 -2
@@ -23,8 +23,20 @@ import {
23
23
  useCreatePost,
24
24
  useSuspensePost,
25
25
  useUpdatePost,
26
+ useDeletePost,
26
27
  } from "../../hooks/blog-hooks";
27
28
  import { slugify } from "../../../utils";
29
+ import {
30
+ AlertDialog,
31
+ AlertDialogAction,
32
+ AlertDialogCancel,
33
+ AlertDialogContent,
34
+ AlertDialogDescription,
35
+ AlertDialogFooter,
36
+ AlertDialogHeader,
37
+ AlertDialogTitle,
38
+ AlertDialogTrigger,
39
+ } from "@workspace/ui/components/alert-dialog";
28
40
 
29
41
  import { zodResolver } from "@hookform/resolvers/zod";
30
42
  import { Loader2 } from "lucide-react";
@@ -404,6 +416,7 @@ type EditPostFormProps = {
404
416
  postSlug: string;
405
417
  onClose: () => void;
406
418
  onSuccess: (post: { slug: string; published: boolean }) => void;
419
+ onDelete?: () => void;
407
420
  };
408
421
 
409
422
  const editPostFormPropsAreEqual = (
@@ -413,6 +426,7 @@ const editPostFormPropsAreEqual = (
413
426
  if (prevProps.postSlug !== nextProps.postSlug) return false;
414
427
  if (prevProps.onClose !== nextProps.onClose) return false;
415
428
  if (prevProps.onSuccess !== nextProps.onSuccess) return false;
429
+ if (prevProps.onDelete !== nextProps.onDelete) return false;
416
430
  return true;
417
431
  };
418
432
 
@@ -420,8 +434,10 @@ const EditPostFormComponent = ({
420
434
  postSlug,
421
435
  onClose,
422
436
  onSuccess,
437
+ onDelete,
423
438
  }: EditPostFormProps) => {
424
439
  const [featuredImageUploading, setFeaturedImageUploading] = useState(false);
440
+ const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
425
441
  const { localization } = usePluginOverrides<
426
442
  BlogPluginOverrides,
427
443
  Partial<BlogPluginOverrides>
@@ -457,6 +473,9 @@ const EditPostFormComponent = ({
457
473
  error: updatePostError,
458
474
  } = useUpdatePost();
459
475
 
476
+ const { mutateAsync: deletePost, isPending: isDeletingPost } =
477
+ useDeletePost();
478
+
460
479
  type EditPostFormValues = z.input<typeof schema>;
461
480
  const onSubmit = async (data: EditPostFormValues) => {
462
481
  // Wait for mutation to complete, including refresh
@@ -489,6 +508,21 @@ const EditPostFormComponent = ({
489
508
  });
490
509
  };
491
510
 
511
+ const handleDelete = async () => {
512
+ if (!post?.id) return;
513
+
514
+ await deletePost({ id: post.id });
515
+ toast.success(localization.BLOG_FORMS_TOAST_DELETE_SUCCESS);
516
+ setDeleteDialogOpen(false);
517
+
518
+ // Call onDelete callback if provided, otherwise use onClose
519
+ if (onDelete) {
520
+ onDelete();
521
+ } else {
522
+ onClose();
523
+ }
524
+ };
525
+
492
526
  const form = useForm<z.input<typeof schema>>({
493
527
  resolver: zodResolver(schema),
494
528
  defaultValues: {
@@ -508,20 +542,64 @@ const EditPostFormComponent = ({
508
542
  }
509
543
 
510
544
  return (
511
- <PostFormBody
512
- form={form}
513
- onSubmit={onSubmit}
514
- submitLabel={
515
- isUpdatingPost
516
- ? localization.BLOG_FORMS_SUBMIT_UPDATE_PENDING
517
- : localization.BLOG_FORMS_SUBMIT_UPDATE_IDLE
518
- }
519
- onCancel={onClose}
520
- disabled={isUpdatingPost || featuredImageUploading}
521
- errorMessage={updatePostError?.message}
522
- setFeaturedImageUploading={setFeaturedImageUploading}
523
- initialSlugTouched={!!post?.slug}
524
- />
545
+ <>
546
+ <PostFormBody
547
+ form={form}
548
+ onSubmit={onSubmit}
549
+ submitLabel={
550
+ isUpdatingPost
551
+ ? localization.BLOG_FORMS_SUBMIT_UPDATE_PENDING
552
+ : localization.BLOG_FORMS_SUBMIT_UPDATE_IDLE
553
+ }
554
+ onCancel={onClose}
555
+ disabled={isUpdatingPost || featuredImageUploading}
556
+ errorMessage={updatePostError?.message}
557
+ setFeaturedImageUploading={setFeaturedImageUploading}
558
+ initialSlugTouched={!!post?.slug}
559
+ />
560
+ <div className="w-full">
561
+ <AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
562
+ <AlertDialogTrigger asChild>
563
+ <Button
564
+ variant="destructive"
565
+ type="button"
566
+ disabled={
567
+ isUpdatingPost || featuredImageUploading || isDeletingPost
568
+ }
569
+ className="mt-4"
570
+ >
571
+ {localization.BLOG_FORMS_DELETE_BUTTON}
572
+ </Button>
573
+ </AlertDialogTrigger>
574
+ <AlertDialogContent>
575
+ <AlertDialogHeader>
576
+ <AlertDialogTitle>
577
+ {localization.BLOG_FORMS_DELETE_DIALOG_TITLE}
578
+ </AlertDialogTitle>
579
+ <AlertDialogDescription>
580
+ {localization.BLOG_FORMS_DELETE_DIALOG_DESCRIPTION}
581
+ </AlertDialogDescription>
582
+ </AlertDialogHeader>
583
+ <AlertDialogFooter>
584
+ <AlertDialogCancel disabled={isDeletingPost}>
585
+ {localization.BLOG_FORMS_DELETE_DIALOG_CANCEL}
586
+ </AlertDialogCancel>
587
+ <AlertDialogAction
588
+ onClick={(e) => {
589
+ e.preventDefault();
590
+ void handleDelete();
591
+ }}
592
+ disabled={isDeletingPost}
593
+ >
594
+ {isDeletingPost
595
+ ? localization.BLOG_FORMS_DELETE_PENDING
596
+ : localization.BLOG_FORMS_DELETE_DIALOG_CONFIRM}
597
+ </AlertDialogAction>
598
+ </AlertDialogFooter>
599
+ </AlertDialogContent>
600
+ </AlertDialog>
601
+ </div>
602
+ </>
525
603
  );
526
604
  };
527
605
 
@@ -44,6 +44,11 @@ export function EditPostPage({ slug }: { slug: string }) {
44
44
  navigate(`${basePath}/blog/${post.slug}`);
45
45
  };
46
46
 
47
+ const handleDelete = () => {
48
+ // Navigate to blog list after deletion
49
+ navigate(`${basePath}/blog`);
50
+ };
51
+
47
52
  return (
48
53
  <PageWrapper className="gap-6" testId="edit-post-page">
49
54
  <PageHeader
@@ -54,6 +59,7 @@ export function EditPostPage({ slug }: { slug: string }) {
54
59
  postSlug={slug}
55
60
  onClose={handleClose}
56
61
  onSuccess={handleSuccess}
62
+ onDelete={handleDelete}
57
63
  />
58
64
  </PageWrapper>
59
65
  );
@@ -438,6 +438,44 @@ export function useUpdatePost() {
438
438
  });
439
439
  }
440
440
 
441
+ /** Delete a post by id */
442
+ export function useDeletePost() {
443
+ const { refresh, apiBaseURL, apiBasePath } =
444
+ usePluginOverrides<BlogPluginOverrides>("blog");
445
+
446
+ const client = createApiClient<BlogApiRouter>({
447
+ baseURL: apiBaseURL,
448
+ basePath: apiBasePath,
449
+ });
450
+
451
+ const queryClient = useQueryClient();
452
+ const queries = createBlogQueryKeys(client);
453
+
454
+ return useMutation<{ success: boolean }, Error, { id: string }>({
455
+ mutationKey: [...queries.posts._def, "delete"],
456
+ mutationFn: async ({ id }: { id: string }) => {
457
+ const response = await client(`@delete/posts/:id`, {
458
+ method: "DELETE",
459
+ params: { id },
460
+ });
461
+ return response.data as { success: boolean };
462
+ },
463
+ onSuccess: async () => {
464
+ // Invalidate all post lists and detail caches - wait for completion
465
+ await queryClient.invalidateQueries({
466
+ queryKey: queries.posts._def,
467
+ });
468
+ await queryClient.invalidateQueries({
469
+ queryKey: queries.drafts.list._def,
470
+ });
471
+ // Refresh server-side cache (Next.js router cache)
472
+ if (refresh) {
473
+ await refresh();
474
+ }
475
+ },
476
+ });
477
+ }
478
+
441
479
  /**
442
480
  * Hook for searching posts by a free-text query. Uses `usePosts` under the hood.
443
481
  * Debounces the query and preserves last successful results to avoid flicker.
@@ -25,8 +25,18 @@ export const BLOG_FORMS = {
25
25
 
26
26
  BLOG_FORMS_TOAST_CREATE_SUCCESS: "Post created successfully",
27
27
  BLOG_FORMS_TOAST_UPDATE_SUCCESS: "Post updated successfully",
28
+ BLOG_FORMS_TOAST_DELETE_SUCCESS: "Post deleted successfully",
28
29
  BLOG_FORMS_LOADING_POST: "Loading post...",
29
30
 
31
+ // Delete post
32
+ BLOG_FORMS_DELETE_BUTTON: "Delete Post",
33
+ BLOG_FORMS_DELETE_DIALOG_TITLE: "Delete Post",
34
+ BLOG_FORMS_DELETE_DIALOG_DESCRIPTION:
35
+ "Are you sure you want to delete this post? This action cannot be undone.",
36
+ BLOG_FORMS_DELETE_DIALOG_CANCEL: "Cancel",
37
+ BLOG_FORMS_DELETE_DIALOG_CONFIRM: "Delete",
38
+ BLOG_FORMS_DELETE_PENDING: "Deleting...",
39
+
30
40
  // Markdown editor
31
41
  BLOG_FORMS_EDITOR_PLACEHOLDER: "Write something...",
32
42
 
@@ -42,10 +42,14 @@ export { createClient } from "better-call/client";
42
42
  * });
43
43
  * ```
44
44
  *
45
- * @template TPlugin - The exact plugin definition (auto-inferred)
45
+ * @template TOverrides - The shape of overridable components/functions this plugin requires
46
+ * @template TRoutes - The exact shape of routes this plugin provides (preserves keys and route types)
46
47
  */
47
48
  export function defineClientPlugin<
48
- TPlugin extends ClientPlugin<any, Record<string, Route>>,
49
- >(plugin: TPlugin): TPlugin {
49
+ TOverrides = Record<string, never>,
50
+ TRoutes extends Record<string, Route> = Record<string, Route>,
51
+ >(
52
+ plugin: ClientPlugin<TOverrides, TRoutes>,
53
+ ): ClientPlugin<TOverrides, TRoutes> {
50
54
  return plugin;
51
55
  }
@@ -36,14 +36,14 @@ interface SerializedTag extends Omit<Tag, "createdAt" | "updatedAt"> {
36
36
 
37
37
  declare const createPostSchema: z.ZodObject<{
38
38
  slug: z.ZodOptional<z.ZodString>;
39
- publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
39
+ published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
40
40
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
41
+ publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
41
42
  updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
42
43
  title: z.ZodString;
43
44
  content: z.ZodString;
44
45
  excerpt: z.ZodString;
45
46
  image: z.ZodOptional<z.ZodString>;
46
- published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
47
47
  tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
48
48
  name: z.ZodString;
49
49
  }, z.core.$strip>, z.ZodObject<{
@@ -36,14 +36,14 @@ interface SerializedTag extends Omit<Tag, "createdAt" | "updatedAt"> {
36
36
 
37
37
  declare const createPostSchema: z.ZodObject<{
38
38
  slug: z.ZodOptional<z.ZodString>;
39
- publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
39
+ published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
40
40
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
41
+ publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
41
42
  updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
42
43
  title: z.ZodString;
43
44
  content: z.ZodString;
44
45
  excerpt: z.ZodString;
45
46
  image: z.ZodOptional<z.ZodString>;
46
- published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
47
47
  tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
48
48
  name: z.ZodString;
49
49
  }, z.core.$strip>, z.ZodObject<{
@@ -36,14 +36,14 @@ interface SerializedTag extends Omit<Tag, "createdAt" | "updatedAt"> {
36
36
 
37
37
  declare const createPostSchema: z.ZodObject<{
38
38
  slug: z.ZodOptional<z.ZodString>;
39
- publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
39
+ published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
40
40
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
41
+ publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
41
42
  updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
42
43
  title: z.ZodString;
43
44
  content: z.ZodString;
44
45
  excerpt: z.ZodString;
45
46
  image: z.ZodOptional<z.ZodString>;
46
- published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
47
47
  tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
48
48
  name: z.ZodString;
49
49
  }, z.core.$strip>, z.ZodObject<{