@lodashventure/medusa-media-manager 0.2.9 → 0.2.11

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.
@@ -704,11 +704,13 @@ const MediaAssetDialog = ({
704
704
  const [visibility, setVisibility] = react.useState("public");
705
705
  const [deletePromptOpen, setDeletePromptOpen] = react.useState(false);
706
706
  const [deleteAttempted, setDeleteAttempted] = react.useState(false);
707
+ const [isDeleting, setIsDeleting] = react.useState(false);
707
708
  react.useEffect(() => {
708
709
  if (!assetId) {
709
710
  setAsset(null);
710
711
  setPreviewUrl(null);
711
712
  setDeleteAttempted(false);
713
+ setDeletePromptOpen(false);
712
714
  return;
713
715
  }
714
716
  let isMounted = true;
@@ -802,6 +804,10 @@ const MediaAssetDialog = ({
802
804
  if (!assetId) {
803
805
  return;
804
806
  }
807
+ if (isDeleting) {
808
+ return;
809
+ }
810
+ setIsDeleting(true);
805
811
  try {
806
812
  await deleteMediaAsset(assetId, force);
807
813
  ui.toast.success("Asset deleted");
@@ -814,6 +820,8 @@ const MediaAssetDialog = ({
814
820
  ui.toast.error("Failed to delete", {
815
821
  description: (err == null ? void 0 : err.message) ?? "Unable to delete asset. If it's in use, try forcing deletion."
816
822
  });
823
+ } finally {
824
+ setIsDeleting(false);
817
825
  }
818
826
  };
819
827
  const handleVariantCopy = async (preset) => {
@@ -859,67 +867,239 @@ const MediaAssetDialog = ({
859
867
  }
860
868
  };
861
869
  const assetRelations = (asset == null ? void 0 : asset.relations) ?? [];
862
- return /* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal, { open: Boolean(assetId), onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(
863
- ui.FocusModal.Content,
864
- {
865
- ref: modalContentRef,
866
- className: "flex h-full w-full max-w-3xl flex-col overflow-y-auto p-6",
867
- children: [
868
- /* @__PURE__ */ jsxRuntime.jsxs(ui.FocusModal.Header, { children: [
869
- /* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Title, { className: "text-lg font-semibold", children: "Asset details" }),
870
- /* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Description, { children: "Review and update metadata, copy delivery URLs, or replace the original file." })
871
- ] }),
872
- isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-ui-fg-subtle", children: "Loading asset..." }) }) : asset ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col gap-y-6", children: [
873
- /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "flex flex-col gap-y-4", children: [
874
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-video w-full overflow-hidden rounded-lg border border-ui-border-base bg-ui-bg-subtle", children: previewUrl ? /* @__PURE__ */ jsxRuntime.jsx(
875
- "img",
876
- {
877
- src: previewUrl,
878
- alt: asset.original_filename,
879
- className: "h-full w-full object-contain"
880
- }
881
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full w-full items-center justify-center text-ui-fg-muted", children: /* @__PURE__ */ jsxRuntime.jsx(icons.Photo, { className: "size-8" }) }) }),
882
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2 text-xs text-ui-fg-subtle", children: [
883
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: asset.mime }),
884
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "·" }),
885
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatFileSize(asset.size_bytes) }),
886
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "·" }),
887
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
888
- "Updated ",
889
- formatDateTime(asset.updated_at)
870
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.FocusModal, { open: Boolean(assetId), onOpenChange: (open) => !open && onClose(), children: [
871
+ /* @__PURE__ */ jsxRuntime.jsxs(
872
+ ui.FocusModal.Content,
873
+ {
874
+ ref: modalContentRef,
875
+ className: "flex h-full w-full max-w-3xl flex-col overflow-y-auto p-6",
876
+ children: [
877
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.FocusModal.Header, { children: [
878
+ /* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Title, { className: "text-lg font-semibold", children: "Asset details" }),
879
+ /* @__PURE__ */ jsxRuntime.jsx(ui.FocusModal.Description, { children: "Review and update metadata, copy delivery URLs, or replace the original file." })
880
+ ] }),
881
+ isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-ui-fg-subtle", children: "Loading asset..." }) }) : asset ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col gap-y-6", children: [
882
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "flex flex-col gap-y-4", children: [
883
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-video w-full overflow-hidden rounded-lg border border-ui-border-base bg-ui-bg-subtle", children: previewUrl ? /* @__PURE__ */ jsxRuntime.jsx(
884
+ "img",
885
+ {
886
+ src: previewUrl,
887
+ alt: asset.original_filename,
888
+ className: "h-full w-full object-contain"
889
+ }
890
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full w-full items-center justify-center text-ui-fg-muted", children: /* @__PURE__ */ jsxRuntime.jsx(icons.Photo, { className: "size-8" }) }) }),
891
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2 text-xs text-ui-fg-subtle", children: [
892
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: asset.mime }),
893
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "·" }),
894
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatFileSize(asset.size_bytes) }),
895
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "·" }),
896
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
897
+ "Updated ",
898
+ formatDateTime(asset.updated_at)
899
+ ] }),
900
+ /* @__PURE__ */ jsxRuntime.jsx(
901
+ ui.StatusBadge,
902
+ {
903
+ size: "small",
904
+ color: STATUS_COLORS[asset.status] ?? "default",
905
+ children: asset.status
906
+ }
907
+ ),
908
+ /* @__PURE__ */ jsxRuntime.jsx(
909
+ ui.Badge,
910
+ {
911
+ size: "small",
912
+ variant: asset.visibility === "public" ? "neutral" : "warning",
913
+ className: "capitalize",
914
+ children: asset.visibility
915
+ }
916
+ )
917
+ ] }),
918
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
919
+ /* @__PURE__ */ jsxRuntime.jsxs(
920
+ ui.Button,
921
+ {
922
+ type: "button",
923
+ variant: "secondary",
924
+ onClick: () => {
925
+ var _a2;
926
+ return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
927
+ },
928
+ isLoading: isReplacing,
929
+ children: [
930
+ /* @__PURE__ */ jsxRuntime.jsx(icons.CircleArrowUp, { className: "mr-1 size-4" }),
931
+ "Replace file"
932
+ ]
933
+ }
934
+ ),
935
+ /* @__PURE__ */ jsxRuntime.jsxs(
936
+ ui.Button,
937
+ {
938
+ type: "button",
939
+ variant: "secondary",
940
+ onClick: () => handleVariantCopy("medium"),
941
+ children: [
942
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowDownTray, { className: "mr-1 size-4" }),
943
+ "Get URL"
944
+ ]
945
+ }
946
+ )
890
947
  ] }),
891
948
  /* @__PURE__ */ jsxRuntime.jsx(
892
- ui.StatusBadge,
949
+ "input",
893
950
  {
894
- size: "small",
895
- color: STATUS_COLORS[asset.status] ?? "default",
896
- children: asset.status
951
+ type: "file",
952
+ ref: fileInputRef,
953
+ className: "hidden",
954
+ onChange: (event) => {
955
+ var _a2;
956
+ return handleReplace(((_a2 = event.target.files) == null ? void 0 : _a2[0]) ?? null);
957
+ }
958
+ }
959
+ )
960
+ ] }),
961
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-4", children: [
962
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2", children: [
963
+ /* @__PURE__ */ jsxRuntime.jsx(
964
+ ui.Input,
965
+ {
966
+ label: "Title",
967
+ placeholder: "Hero banner",
968
+ value: title,
969
+ onChange: (event) => setTitle(event.target.value)
970
+ }
971
+ ),
972
+ /* @__PURE__ */ jsxRuntime.jsx(
973
+ ui.Input,
974
+ {
975
+ label: "Alt text",
976
+ placeholder: "Describe the media for accessibility",
977
+ value: altText,
978
+ onChange: (event) => setAltText(event.target.value)
979
+ }
980
+ )
981
+ ] }),
982
+ /* @__PURE__ */ jsxRuntime.jsx(
983
+ ui.Textarea,
984
+ {
985
+ label: "Caption",
986
+ placeholder: "Optional caption or notes",
987
+ value: caption,
988
+ onChange: (event) => setCaption(event.target.value)
897
989
  }
898
990
  ),
899
991
  /* @__PURE__ */ jsxRuntime.jsx(
900
- ui.Badge,
992
+ ui.Input,
901
993
  {
902
- size: "small",
903
- variant: asset.visibility === "public" ? "neutral" : "warning",
904
- className: "capitalize",
905
- children: asset.visibility
994
+ label: "Tags",
995
+ placeholder: "summer, lookbook, hero",
996
+ value: tagsInput,
997
+ onChange: (event) => setTagsInput(event.target.value),
998
+ helperText: "Separate tags with commas"
906
999
  }
907
- )
1000
+ ),
1001
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
1002
+ /* @__PURE__ */ jsxRuntime.jsxs(
1003
+ ui.Select,
1004
+ {
1005
+ value: status,
1006
+ onValueChange: (value) => setStatus(value),
1007
+ children: [
1008
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Status" }) }),
1009
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
1010
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "draft", children: "Draft" }),
1011
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "published", children: "Published" }),
1012
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "archived", children: "Archived" })
1013
+ ] })
1014
+ ]
1015
+ }
1016
+ ),
1017
+ /* @__PURE__ */ jsxRuntime.jsxs(
1018
+ ui.Select,
1019
+ {
1020
+ value: visibility,
1021
+ onValueChange: (value) => setVisibility(value),
1022
+ children: [
1023
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Visibility" }) }),
1024
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
1025
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "public", children: "Public" }),
1026
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "private", children: "Private" })
1027
+ ] })
1028
+ ]
1029
+ }
1030
+ )
1031
+ ] })
908
1032
  ] }),
909
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
1033
+ ((_a = asset.variants) == null ? void 0 : _a.length) ? /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-3", children: [
1034
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-medium", children: "Variants" }),
1035
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
1036
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
1037
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Name" }),
1038
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Format" }),
1039
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Dimensions" }),
1040
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Size" }),
1041
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "Actions" })
1042
+ ] }) }),
1043
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: asset.variants.map((variant) => /* @__PURE__ */ jsxRuntime.jsxs(
1044
+ ui.Table.Row,
1045
+ {
1046
+ children: [
1047
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "capitalize", children: variant.preset }),
1048
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: variant.format }),
1049
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: variant.width && variant.height ? `${variant.width}×${variant.height}` : "-" }),
1050
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: formatFileSize(variant.size_bytes) }),
1051
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "flex justify-end gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
1052
+ ui.Button,
1053
+ {
1054
+ size: "small",
1055
+ variant: "secondary",
1056
+ onClick: () => handleVariantCopy(variant.preset),
1057
+ children: [
1058
+ /* @__PURE__ */ jsxRuntime.jsx(icons.SquareTwoStack, { className: "mr-1 size-4" }),
1059
+ " Copy URL"
1060
+ ]
1061
+ }
1062
+ ) })
1063
+ ]
1064
+ },
1065
+ variant.id ?? `${variant.preset}-${variant.format}`
1066
+ )) })
1067
+ ] })
1068
+ ] }) : null,
1069
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-3", children: [
1070
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-medium", children: "Usage" }),
1071
+ assetRelations.length ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
1072
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
1073
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Entity" }),
1074
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Reference" }),
1075
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Role" })
1076
+ ] }) }),
1077
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: assetRelations.map((relation) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
1078
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "capitalize", children: relation.entity_type }),
1079
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "font-mono text-xs", children: relation.entity_id }),
1080
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: relation.relation_role })
1081
+ ] }, relation.id)) })
1082
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-ui-fg-subtle", children: "This asset is not linked to any entities." })
1083
+ ] })
1084
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center text-sm text-ui-fg-subtle", children: "Select an asset to view details." }),
1085
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.FocusModal.Footer, { className: "mt-6 flex flex-wrap items-center justify-between gap-3", children: [
1086
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
910
1087
  /* @__PURE__ */ jsxRuntime.jsxs(
911
1088
  ui.Button,
912
1089
  {
913
1090
  type: "button",
914
- variant: "secondary",
1091
+ variant: "danger",
915
1092
  onClick: () => {
916
- var _a2;
917
- return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
1093
+ if (!asset) {
1094
+ return;
1095
+ }
1096
+ setDeletePromptOpen(true);
918
1097
  },
919
- isLoading: isReplacing,
1098
+ disabled: !asset || isDeleting,
1099
+ isLoading: isDeleting,
920
1100
  children: [
921
- /* @__PURE__ */ jsxRuntime.jsx(icons.CircleArrowUp, { className: "mr-1 size-4" }),
922
- "Replace file"
1101
+ /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "mr-1 size-4" }),
1102
+ " Delete"
923
1103
  ]
924
1104
  }
925
1105
  ),
@@ -928,227 +1108,82 @@ const MediaAssetDialog = ({
928
1108
  {
929
1109
  type: "button",
930
1110
  variant: "secondary",
931
- onClick: () => handleVariantCopy("medium"),
1111
+ onClick: () => asset && handleVariantCopy("medium"),
1112
+ disabled: !asset,
932
1113
  children: [
933
- /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowDownTray, { className: "mr-1 size-4" }),
934
- "Get URL"
1114
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowPath, { className: "mr-1 size-4" }),
1115
+ " Copy URL"
935
1116
  ]
936
1117
  }
937
1118
  )
938
1119
  ] }),
939
- /* @__PURE__ */ jsxRuntime.jsx(
940
- "input",
941
- {
942
- type: "file",
943
- ref: fileInputRef,
944
- className: "hidden",
945
- onChange: (event) => {
946
- var _a2;
947
- return handleReplace(((_a2 = event.target.files) == null ? void 0 : _a2[0]) ?? null);
948
- }
949
- }
950
- )
951
- ] }),
952
- /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-4", children: [
953
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2", children: [
954
- /* @__PURE__ */ jsxRuntime.jsx(
955
- ui.Input,
956
- {
957
- label: "Title",
958
- placeholder: "Hero banner",
959
- value: title,
960
- onChange: (event) => setTitle(event.target.value)
961
- }
962
- ),
1120
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
1121
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", onClick: onClose, children: "Close" }),
963
1122
  /* @__PURE__ */ jsxRuntime.jsx(
964
- ui.Input,
965
- {
966
- label: "Alt text",
967
- placeholder: "Describe the media for accessibility",
968
- value: altText,
969
- onChange: (event) => setAltText(event.target.value)
970
- }
971
- )
972
- ] }),
973
- /* @__PURE__ */ jsxRuntime.jsx(
974
- ui.Textarea,
975
- {
976
- label: "Caption",
977
- placeholder: "Optional caption or notes",
978
- value: caption,
979
- onChange: (event) => setCaption(event.target.value)
980
- }
981
- ),
982
- /* @__PURE__ */ jsxRuntime.jsx(
983
- ui.Input,
984
- {
985
- label: "Tags",
986
- placeholder: "summer, lookbook, hero",
987
- value: tagsInput,
988
- onChange: (event) => setTagsInput(event.target.value),
989
- helperText: "Separate tags with commas"
990
- }
991
- ),
992
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
993
- /* @__PURE__ */ jsxRuntime.jsxs(
994
- ui.Select,
1123
+ ui.Button,
995
1124
  {
996
- value: status,
997
- onValueChange: (value) => setStatus(value),
998
- children: [
999
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Status" }) }),
1000
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
1001
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "draft", children: "Draft" }),
1002
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "published", children: "Published" }),
1003
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "archived", children: "Archived" })
1004
- ] })
1005
- ]
1006
- }
1007
- ),
1008
- /* @__PURE__ */ jsxRuntime.jsxs(
1009
- ui.Select,
1010
- {
1011
- value: visibility,
1012
- onValueChange: (value) => setVisibility(value),
1013
- children: [
1014
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Visibility" }) }),
1015
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
1016
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "public", children: "Public" }),
1017
- /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "private", children: "Private" })
1018
- ] })
1019
- ]
1125
+ onClick: handleSave,
1126
+ disabled: !asset || isSaving,
1127
+ isLoading: isSaving,
1128
+ children: "Save changes"
1020
1129
  }
1021
1130
  )
1022
1131
  ] })
1023
1132
  ] }),
1024
- ((_a = asset.variants) == null ? void 0 : _a.length) ? /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-3", children: [
1025
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-medium", children: "Variants" }),
1026
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
1027
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
1028
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Name" }),
1029
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Format" }),
1030
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Dimensions" }),
1031
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Size" }),
1032
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "Actions" })
1033
- ] }) }),
1034
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: asset.variants.map((variant) => /* @__PURE__ */ jsxRuntime.jsxs(
1035
- ui.Table.Row,
1036
- {
1037
- children: [
1038
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "capitalize", children: variant.preset }),
1039
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: variant.format }),
1040
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: variant.width && variant.height ? `${variant.width}×${variant.height}` : "-" }),
1041
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: formatFileSize(variant.size_bytes) }),
1042
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "flex justify-end gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs(
1043
- ui.Button,
1044
- {
1045
- size: "small",
1046
- variant: "secondary",
1047
- onClick: () => handleVariantCopy(variant.preset),
1048
- children: [
1049
- /* @__PURE__ */ jsxRuntime.jsx(icons.SquareTwoStack, { className: "mr-1 size-4" }),
1050
- " Copy URL"
1051
- ]
1052
- }
1053
- ) })
1054
- ]
1055
- },
1056
- variant.id ?? `${variant.preset}-${variant.format}`
1057
- )) })
1058
- ] })
1059
- ] }) : null,
1060
- /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-3", children: [
1061
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-medium", children: "Usage" }),
1062
- assetRelations.length ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
1063
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
1064
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Entity" }),
1065
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Reference" }),
1066
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Role" })
1067
- ] }) }),
1068
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: assetRelations.map((relation) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
1069
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "capitalize", children: relation.entity_type }),
1070
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "font-mono text-xs", children: relation.entity_id }),
1071
- /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: relation.relation_role })
1072
- ] }, relation.id)) })
1073
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-ui-fg-subtle", children: "This asset is not linked to any entities." })
1074
- ] })
1075
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center text-sm text-ui-fg-subtle", children: "Select an asset to view details." }),
1076
- /* @__PURE__ */ jsxRuntime.jsxs(ui.FocusModal.Footer, { className: "mt-6 flex flex-wrap items-center justify-between gap-3", children: [
1077
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
1078
- /* @__PURE__ */ jsxRuntime.jsxs(
1133
+ deleteAttempted && asset && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 rounded-md border border-ui-border-base bg-ui-bg-subtle p-4 text-sm text-ui-fg-subtle", children: [
1134
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium text-ui-fg-base", children: "Having trouble deleting?" }),
1135
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1", children: "If the asset is still referenced, remove those relations first or attempt a force delete." }),
1136
+ /* @__PURE__ */ jsxRuntime.jsx(
1079
1137
  ui.Button,
1080
1138
  {
1081
- type: "button",
1139
+ className: "mt-3",
1082
1140
  variant: "danger",
1141
+ size: "small",
1142
+ disabled: isDeleting,
1143
+ isLoading: isDeleting,
1083
1144
  onClick: () => {
1084
- if (!asset) {
1085
- return;
1086
- }
1087
- const confirmDelete = window.confirm(
1088
- "Delete this asset and all generated variants?"
1145
+ const confirmForce = window.confirm(
1146
+ "Force delete will remove the asset even if it is still referenced. Continue?"
1089
1147
  );
1090
- if (confirmDelete) {
1091
- handleDelete(false);
1148
+ if (confirmForce) {
1149
+ handleDelete(true);
1092
1150
  }
1093
1151
  },
1094
- disabled: !asset,
1095
- children: [
1096
- /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "mr-1 size-4" }),
1097
- " Delete"
1098
- ]
1099
- }
1100
- ),
1101
- /* @__PURE__ */ jsxRuntime.jsxs(
1102
- ui.Button,
1103
- {
1104
- type: "button",
1105
- variant: "secondary",
1106
- onClick: () => asset && handleVariantCopy("medium"),
1107
- disabled: !asset,
1108
- children: [
1109
- /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowPath, { className: "mr-1 size-4" }),
1110
- " Copy URL"
1111
- ]
1152
+ children: "Force delete"
1112
1153
  }
1113
1154
  )
1155
+ ] })
1156
+ ]
1157
+ }
1158
+ ),
1159
+ /* @__PURE__ */ jsxRuntime.jsx(
1160
+ ui.Prompt,
1161
+ {
1162
+ variant: "confirmation",
1163
+ open: Boolean(asset) && deletePromptOpen,
1164
+ onOpenChange: (open) => setDeletePromptOpen(open),
1165
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Content, { children: [
1166
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Header, { children: [
1167
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Title, { children: "Delete asset" }),
1168
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Description, { children: asset ? `Delete "${asset.original_filename}" and all generated variants? This cannot be undone.` : "Delete this asset and all generated variants? This cannot be undone." })
1114
1169
  ] }),
1115
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
1116
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", onClick: onClose, children: "Close" }),
1117
- /* @__PURE__ */ jsxRuntime.jsx(
1170
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Prompt.Footer, { children: [
1171
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Cancel, { children: "Cancel" }),
1172
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Prompt.Action, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
1118
1173
  ui.Button,
1119
1174
  {
1120
- onClick: handleSave,
1121
- disabled: !asset || isSaving,
1122
- isLoading: isSaving,
1123
- children: "Save changes"
1175
+ variant: "danger",
1176
+ onClick: () => handleDelete(false),
1177
+ disabled: isDeleting,
1178
+ isLoading: isDeleting,
1179
+ children: "Delete"
1124
1180
  }
1125
- )
1181
+ ) })
1126
1182
  ] })
1127
- ] }),
1128
- deleteAttempted && asset && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 rounded-md border border-ui-border-base bg-ui-bg-subtle p-4 text-sm text-ui-fg-subtle", children: [
1129
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium text-ui-fg-base", children: "Having trouble deleting?" }),
1130
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1", children: "If the asset is still referenced, remove those relations first or attempt a force delete." }),
1131
- /* @__PURE__ */ jsxRuntime.jsx(
1132
- ui.Button,
1133
- {
1134
- className: "mt-3",
1135
- variant: "danger",
1136
- size: "small",
1137
- onClick: () => {
1138
- const confirmForce = window.confirm(
1139
- "Force delete will remove the asset even if it is still referenced. Continue?"
1140
- );
1141
- if (confirmForce) {
1142
- handleDelete(true);
1143
- }
1144
- },
1145
- children: "Force delete"
1146
- }
1147
- )
1148
1183
  ] })
1149
- ]
1150
- }
1151
- ) });
1184
+ }
1185
+ )
1186
+ ] });
1152
1187
  };
1153
1188
  const MediaAssetDrawer = MediaAssetDialog;
1154
1189
  const PAGE_SIZE = 24;
@@ -2,7 +2,7 @@ import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { useState, useMemo, useCallback, useRef, useEffect } from "react";
3
3
  import { defineRouteConfig } from "@medusajs/admin-sdk";
4
4
  import { SquaresPlus, ListBullet, ArrowPath, CloudArrowUp, Photo, ShieldCheck, DocumentText, PlaySolid, X, CircleArrowUp, ArrowDownTray, SquareTwoStack, Trash } from "@medusajs/icons";
5
- import { Input, Select, TooltipProvider, Tooltip, IconButton, clx, Button, Badge, Skeleton, Table, StatusBadge, toast, Drawer, FocusModal, Textarea, Container, Heading, Alert } from "@medusajs/ui";
5
+ import { Input, Select, TooltipProvider, Tooltip, IconButton, clx, Button, Badge, Skeleton, Table, StatusBadge, toast, Drawer, FocusModal, Textarea, Prompt, Container, Heading, Alert } from "@medusajs/ui";
6
6
  import "@medusajs/admin-shared";
7
7
  const ALL_OPTION_VALUE = "all";
8
8
  const normalizeSelectValue = (value) => {
@@ -703,11 +703,13 @@ const MediaAssetDialog = ({
703
703
  const [visibility, setVisibility] = useState("public");
704
704
  const [deletePromptOpen, setDeletePromptOpen] = useState(false);
705
705
  const [deleteAttempted, setDeleteAttempted] = useState(false);
706
+ const [isDeleting, setIsDeleting] = useState(false);
706
707
  useEffect(() => {
707
708
  if (!assetId) {
708
709
  setAsset(null);
709
710
  setPreviewUrl(null);
710
711
  setDeleteAttempted(false);
712
+ setDeletePromptOpen(false);
711
713
  return;
712
714
  }
713
715
  let isMounted = true;
@@ -801,6 +803,10 @@ const MediaAssetDialog = ({
801
803
  if (!assetId) {
802
804
  return;
803
805
  }
806
+ if (isDeleting) {
807
+ return;
808
+ }
809
+ setIsDeleting(true);
804
810
  try {
805
811
  await deleteMediaAsset(assetId, force);
806
812
  toast.success("Asset deleted");
@@ -813,6 +819,8 @@ const MediaAssetDialog = ({
813
819
  toast.error("Failed to delete", {
814
820
  description: (err == null ? void 0 : err.message) ?? "Unable to delete asset. If it's in use, try forcing deletion."
815
821
  });
822
+ } finally {
823
+ setIsDeleting(false);
816
824
  }
817
825
  };
818
826
  const handleVariantCopy = async (preset) => {
@@ -858,67 +866,239 @@ const MediaAssetDialog = ({
858
866
  }
859
867
  };
860
868
  const assetRelations = (asset == null ? void 0 : asset.relations) ?? [];
861
- return /* @__PURE__ */ jsx(FocusModal, { open: Boolean(assetId), onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs(
862
- FocusModal.Content,
863
- {
864
- ref: modalContentRef,
865
- className: "flex h-full w-full max-w-3xl flex-col overflow-y-auto p-6",
866
- children: [
867
- /* @__PURE__ */ jsxs(FocusModal.Header, { children: [
868
- /* @__PURE__ */ jsx(FocusModal.Title, { className: "text-lg font-semibold", children: "Asset details" }),
869
- /* @__PURE__ */ jsx(FocusModal.Description, { children: "Review and update metadata, copy delivery URLs, or replace the original file." })
870
- ] }),
871
- isLoading ? /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "text-sm text-ui-fg-subtle", children: "Loading asset..." }) }) : asset ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-y-6", children: [
872
- /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-y-4", children: [
873
- /* @__PURE__ */ jsx("div", { className: "aspect-video w-full overflow-hidden rounded-lg border border-ui-border-base bg-ui-bg-subtle", children: previewUrl ? /* @__PURE__ */ jsx(
874
- "img",
875
- {
876
- src: previewUrl,
877
- alt: asset.original_filename,
878
- className: "h-full w-full object-contain"
879
- }
880
- ) : /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center text-ui-fg-muted", children: /* @__PURE__ */ jsx(Photo, { className: "size-8" }) }) }),
881
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 text-xs text-ui-fg-subtle", children: [
882
- /* @__PURE__ */ jsx("span", { children: asset.mime }),
883
- /* @__PURE__ */ jsx("span", { children: "·" }),
884
- /* @__PURE__ */ jsx("span", { children: formatFileSize(asset.size_bytes) }),
885
- /* @__PURE__ */ jsx("span", { children: "·" }),
886
- /* @__PURE__ */ jsxs("span", { children: [
887
- "Updated ",
888
- formatDateTime(asset.updated_at)
869
+ return /* @__PURE__ */ jsxs(FocusModal, { open: Boolean(assetId), onOpenChange: (open) => !open && onClose(), children: [
870
+ /* @__PURE__ */ jsxs(
871
+ FocusModal.Content,
872
+ {
873
+ ref: modalContentRef,
874
+ className: "flex h-full w-full max-w-3xl flex-col overflow-y-auto p-6",
875
+ children: [
876
+ /* @__PURE__ */ jsxs(FocusModal.Header, { children: [
877
+ /* @__PURE__ */ jsx(FocusModal.Title, { className: "text-lg font-semibold", children: "Asset details" }),
878
+ /* @__PURE__ */ jsx(FocusModal.Description, { children: "Review and update metadata, copy delivery URLs, or replace the original file." })
879
+ ] }),
880
+ isLoading ? /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "text-sm text-ui-fg-subtle", children: "Loading asset..." }) }) : asset ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-y-6", children: [
881
+ /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-y-4", children: [
882
+ /* @__PURE__ */ jsx("div", { className: "aspect-video w-full overflow-hidden rounded-lg border border-ui-border-base bg-ui-bg-subtle", children: previewUrl ? /* @__PURE__ */ jsx(
883
+ "img",
884
+ {
885
+ src: previewUrl,
886
+ alt: asset.original_filename,
887
+ className: "h-full w-full object-contain"
888
+ }
889
+ ) : /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center text-ui-fg-muted", children: /* @__PURE__ */ jsx(Photo, { className: "size-8" }) }) }),
890
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 text-xs text-ui-fg-subtle", children: [
891
+ /* @__PURE__ */ jsx("span", { children: asset.mime }),
892
+ /* @__PURE__ */ jsx("span", { children: "·" }),
893
+ /* @__PURE__ */ jsx("span", { children: formatFileSize(asset.size_bytes) }),
894
+ /* @__PURE__ */ jsx("span", { children: "·" }),
895
+ /* @__PURE__ */ jsxs("span", { children: [
896
+ "Updated ",
897
+ formatDateTime(asset.updated_at)
898
+ ] }),
899
+ /* @__PURE__ */ jsx(
900
+ StatusBadge,
901
+ {
902
+ size: "small",
903
+ color: STATUS_COLORS[asset.status] ?? "default",
904
+ children: asset.status
905
+ }
906
+ ),
907
+ /* @__PURE__ */ jsx(
908
+ Badge,
909
+ {
910
+ size: "small",
911
+ variant: asset.visibility === "public" ? "neutral" : "warning",
912
+ className: "capitalize",
913
+ children: asset.visibility
914
+ }
915
+ )
916
+ ] }),
917
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
918
+ /* @__PURE__ */ jsxs(
919
+ Button,
920
+ {
921
+ type: "button",
922
+ variant: "secondary",
923
+ onClick: () => {
924
+ var _a2;
925
+ return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
926
+ },
927
+ isLoading: isReplacing,
928
+ children: [
929
+ /* @__PURE__ */ jsx(CircleArrowUp, { className: "mr-1 size-4" }),
930
+ "Replace file"
931
+ ]
932
+ }
933
+ ),
934
+ /* @__PURE__ */ jsxs(
935
+ Button,
936
+ {
937
+ type: "button",
938
+ variant: "secondary",
939
+ onClick: () => handleVariantCopy("medium"),
940
+ children: [
941
+ /* @__PURE__ */ jsx(ArrowDownTray, { className: "mr-1 size-4" }),
942
+ "Get URL"
943
+ ]
944
+ }
945
+ )
889
946
  ] }),
890
947
  /* @__PURE__ */ jsx(
891
- StatusBadge,
948
+ "input",
892
949
  {
893
- size: "small",
894
- color: STATUS_COLORS[asset.status] ?? "default",
895
- children: asset.status
950
+ type: "file",
951
+ ref: fileInputRef,
952
+ className: "hidden",
953
+ onChange: (event) => {
954
+ var _a2;
955
+ return handleReplace(((_a2 = event.target.files) == null ? void 0 : _a2[0]) ?? null);
956
+ }
957
+ }
958
+ )
959
+ ] }),
960
+ /* @__PURE__ */ jsxs("section", { className: "space-y-4", children: [
961
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2", children: [
962
+ /* @__PURE__ */ jsx(
963
+ Input,
964
+ {
965
+ label: "Title",
966
+ placeholder: "Hero banner",
967
+ value: title,
968
+ onChange: (event) => setTitle(event.target.value)
969
+ }
970
+ ),
971
+ /* @__PURE__ */ jsx(
972
+ Input,
973
+ {
974
+ label: "Alt text",
975
+ placeholder: "Describe the media for accessibility",
976
+ value: altText,
977
+ onChange: (event) => setAltText(event.target.value)
978
+ }
979
+ )
980
+ ] }),
981
+ /* @__PURE__ */ jsx(
982
+ Textarea,
983
+ {
984
+ label: "Caption",
985
+ placeholder: "Optional caption or notes",
986
+ value: caption,
987
+ onChange: (event) => setCaption(event.target.value)
896
988
  }
897
989
  ),
898
990
  /* @__PURE__ */ jsx(
899
- Badge,
991
+ Input,
900
992
  {
901
- size: "small",
902
- variant: asset.visibility === "public" ? "neutral" : "warning",
903
- className: "capitalize",
904
- children: asset.visibility
993
+ label: "Tags",
994
+ placeholder: "summer, lookbook, hero",
995
+ value: tagsInput,
996
+ onChange: (event) => setTagsInput(event.target.value),
997
+ helperText: "Separate tags with commas"
905
998
  }
906
- )
999
+ ),
1000
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
1001
+ /* @__PURE__ */ jsxs(
1002
+ Select,
1003
+ {
1004
+ value: status,
1005
+ onValueChange: (value) => setStatus(value),
1006
+ children: [
1007
+ /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "Status" }) }),
1008
+ /* @__PURE__ */ jsxs(Select.Content, { children: [
1009
+ /* @__PURE__ */ jsx(Select.Item, { value: "draft", children: "Draft" }),
1010
+ /* @__PURE__ */ jsx(Select.Item, { value: "published", children: "Published" }),
1011
+ /* @__PURE__ */ jsx(Select.Item, { value: "archived", children: "Archived" })
1012
+ ] })
1013
+ ]
1014
+ }
1015
+ ),
1016
+ /* @__PURE__ */ jsxs(
1017
+ Select,
1018
+ {
1019
+ value: visibility,
1020
+ onValueChange: (value) => setVisibility(value),
1021
+ children: [
1022
+ /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "Visibility" }) }),
1023
+ /* @__PURE__ */ jsxs(Select.Content, { children: [
1024
+ /* @__PURE__ */ jsx(Select.Item, { value: "public", children: "Public" }),
1025
+ /* @__PURE__ */ jsx(Select.Item, { value: "private", children: "Private" })
1026
+ ] })
1027
+ ]
1028
+ }
1029
+ )
1030
+ ] })
907
1031
  ] }),
908
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
1032
+ ((_a = asset.variants) == null ? void 0 : _a.length) ? /* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
1033
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: "Variants" }),
1034
+ /* @__PURE__ */ jsxs(Table, { children: [
1035
+ /* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
1036
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Name" }),
1037
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Format" }),
1038
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Dimensions" }),
1039
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Size" }),
1040
+ /* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "Actions" })
1041
+ ] }) }),
1042
+ /* @__PURE__ */ jsx(Table.Body, { children: asset.variants.map((variant) => /* @__PURE__ */ jsxs(
1043
+ Table.Row,
1044
+ {
1045
+ children: [
1046
+ /* @__PURE__ */ jsx(Table.Cell, { className: "capitalize", children: variant.preset }),
1047
+ /* @__PURE__ */ jsx(Table.Cell, { children: variant.format }),
1048
+ /* @__PURE__ */ jsx(Table.Cell, { children: variant.width && variant.height ? `${variant.width}×${variant.height}` : "-" }),
1049
+ /* @__PURE__ */ jsx(Table.Cell, { children: formatFileSize(variant.size_bytes) }),
1050
+ /* @__PURE__ */ jsx(Table.Cell, { className: "flex justify-end gap-2", children: /* @__PURE__ */ jsxs(
1051
+ Button,
1052
+ {
1053
+ size: "small",
1054
+ variant: "secondary",
1055
+ onClick: () => handleVariantCopy(variant.preset),
1056
+ children: [
1057
+ /* @__PURE__ */ jsx(SquareTwoStack, { className: "mr-1 size-4" }),
1058
+ " Copy URL"
1059
+ ]
1060
+ }
1061
+ ) })
1062
+ ]
1063
+ },
1064
+ variant.id ?? `${variant.preset}-${variant.format}`
1065
+ )) })
1066
+ ] })
1067
+ ] }) : null,
1068
+ /* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
1069
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: "Usage" }),
1070
+ assetRelations.length ? /* @__PURE__ */ jsxs(Table, { children: [
1071
+ /* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
1072
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Entity" }),
1073
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Reference" }),
1074
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Role" })
1075
+ ] }) }),
1076
+ /* @__PURE__ */ jsx(Table.Body, { children: assetRelations.map((relation) => /* @__PURE__ */ jsxs(Table.Row, { children: [
1077
+ /* @__PURE__ */ jsx(Table.Cell, { className: "capitalize", children: relation.entity_type }),
1078
+ /* @__PURE__ */ jsx(Table.Cell, { className: "font-mono text-xs", children: relation.entity_id }),
1079
+ /* @__PURE__ */ jsx(Table.Cell, { children: relation.relation_role })
1080
+ ] }, relation.id)) })
1081
+ ] }) : /* @__PURE__ */ jsx("p", { className: "text-sm text-ui-fg-subtle", children: "This asset is not linked to any entities." })
1082
+ ] })
1083
+ ] }) : /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center text-sm text-ui-fg-subtle", children: "Select an asset to view details." }),
1084
+ /* @__PURE__ */ jsxs(FocusModal.Footer, { className: "mt-6 flex flex-wrap items-center justify-between gap-3", children: [
1085
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
909
1086
  /* @__PURE__ */ jsxs(
910
1087
  Button,
911
1088
  {
912
1089
  type: "button",
913
- variant: "secondary",
1090
+ variant: "danger",
914
1091
  onClick: () => {
915
- var _a2;
916
- return (_a2 = fileInputRef.current) == null ? void 0 : _a2.click();
1092
+ if (!asset) {
1093
+ return;
1094
+ }
1095
+ setDeletePromptOpen(true);
917
1096
  },
918
- isLoading: isReplacing,
1097
+ disabled: !asset || isDeleting,
1098
+ isLoading: isDeleting,
919
1099
  children: [
920
- /* @__PURE__ */ jsx(CircleArrowUp, { className: "mr-1 size-4" }),
921
- "Replace file"
1100
+ /* @__PURE__ */ jsx(Trash, { className: "mr-1 size-4" }),
1101
+ " Delete"
922
1102
  ]
923
1103
  }
924
1104
  ),
@@ -927,227 +1107,82 @@ const MediaAssetDialog = ({
927
1107
  {
928
1108
  type: "button",
929
1109
  variant: "secondary",
930
- onClick: () => handleVariantCopy("medium"),
1110
+ onClick: () => asset && handleVariantCopy("medium"),
1111
+ disabled: !asset,
931
1112
  children: [
932
- /* @__PURE__ */ jsx(ArrowDownTray, { className: "mr-1 size-4" }),
933
- "Get URL"
1113
+ /* @__PURE__ */ jsx(ArrowPath, { className: "mr-1 size-4" }),
1114
+ " Copy URL"
934
1115
  ]
935
1116
  }
936
1117
  )
937
1118
  ] }),
938
- /* @__PURE__ */ jsx(
939
- "input",
940
- {
941
- type: "file",
942
- ref: fileInputRef,
943
- className: "hidden",
944
- onChange: (event) => {
945
- var _a2;
946
- return handleReplace(((_a2 = event.target.files) == null ? void 0 : _a2[0]) ?? null);
947
- }
948
- }
949
- )
950
- ] }),
951
- /* @__PURE__ */ jsxs("section", { className: "space-y-4", children: [
952
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2", children: [
953
- /* @__PURE__ */ jsx(
954
- Input,
955
- {
956
- label: "Title",
957
- placeholder: "Hero banner",
958
- value: title,
959
- onChange: (event) => setTitle(event.target.value)
960
- }
961
- ),
1119
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1120
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onClose, children: "Close" }),
962
1121
  /* @__PURE__ */ jsx(
963
- Input,
964
- {
965
- label: "Alt text",
966
- placeholder: "Describe the media for accessibility",
967
- value: altText,
968
- onChange: (event) => setAltText(event.target.value)
969
- }
970
- )
971
- ] }),
972
- /* @__PURE__ */ jsx(
973
- Textarea,
974
- {
975
- label: "Caption",
976
- placeholder: "Optional caption or notes",
977
- value: caption,
978
- onChange: (event) => setCaption(event.target.value)
979
- }
980
- ),
981
- /* @__PURE__ */ jsx(
982
- Input,
983
- {
984
- label: "Tags",
985
- placeholder: "summer, lookbook, hero",
986
- value: tagsInput,
987
- onChange: (event) => setTagsInput(event.target.value),
988
- helperText: "Separate tags with commas"
989
- }
990
- ),
991
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
992
- /* @__PURE__ */ jsxs(
993
- Select,
1122
+ Button,
994
1123
  {
995
- value: status,
996
- onValueChange: (value) => setStatus(value),
997
- children: [
998
- /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "Status" }) }),
999
- /* @__PURE__ */ jsxs(Select.Content, { children: [
1000
- /* @__PURE__ */ jsx(Select.Item, { value: "draft", children: "Draft" }),
1001
- /* @__PURE__ */ jsx(Select.Item, { value: "published", children: "Published" }),
1002
- /* @__PURE__ */ jsx(Select.Item, { value: "archived", children: "Archived" })
1003
- ] })
1004
- ]
1005
- }
1006
- ),
1007
- /* @__PURE__ */ jsxs(
1008
- Select,
1009
- {
1010
- value: visibility,
1011
- onValueChange: (value) => setVisibility(value),
1012
- children: [
1013
- /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "Visibility" }) }),
1014
- /* @__PURE__ */ jsxs(Select.Content, { children: [
1015
- /* @__PURE__ */ jsx(Select.Item, { value: "public", children: "Public" }),
1016
- /* @__PURE__ */ jsx(Select.Item, { value: "private", children: "Private" })
1017
- ] })
1018
- ]
1124
+ onClick: handleSave,
1125
+ disabled: !asset || isSaving,
1126
+ isLoading: isSaving,
1127
+ children: "Save changes"
1019
1128
  }
1020
1129
  )
1021
1130
  ] })
1022
1131
  ] }),
1023
- ((_a = asset.variants) == null ? void 0 : _a.length) ? /* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
1024
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: "Variants" }),
1025
- /* @__PURE__ */ jsxs(Table, { children: [
1026
- /* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
1027
- /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Name" }),
1028
- /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Format" }),
1029
- /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Dimensions" }),
1030
- /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Size" }),
1031
- /* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "Actions" })
1032
- ] }) }),
1033
- /* @__PURE__ */ jsx(Table.Body, { children: asset.variants.map((variant) => /* @__PURE__ */ jsxs(
1034
- Table.Row,
1035
- {
1036
- children: [
1037
- /* @__PURE__ */ jsx(Table.Cell, { className: "capitalize", children: variant.preset }),
1038
- /* @__PURE__ */ jsx(Table.Cell, { children: variant.format }),
1039
- /* @__PURE__ */ jsx(Table.Cell, { children: variant.width && variant.height ? `${variant.width}×${variant.height}` : "-" }),
1040
- /* @__PURE__ */ jsx(Table.Cell, { children: formatFileSize(variant.size_bytes) }),
1041
- /* @__PURE__ */ jsx(Table.Cell, { className: "flex justify-end gap-2", children: /* @__PURE__ */ jsxs(
1042
- Button,
1043
- {
1044
- size: "small",
1045
- variant: "secondary",
1046
- onClick: () => handleVariantCopy(variant.preset),
1047
- children: [
1048
- /* @__PURE__ */ jsx(SquareTwoStack, { className: "mr-1 size-4" }),
1049
- " Copy URL"
1050
- ]
1051
- }
1052
- ) })
1053
- ]
1054
- },
1055
- variant.id ?? `${variant.preset}-${variant.format}`
1056
- )) })
1057
- ] })
1058
- ] }) : null,
1059
- /* @__PURE__ */ jsxs("section", { className: "space-y-3", children: [
1060
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium", children: "Usage" }),
1061
- assetRelations.length ? /* @__PURE__ */ jsxs(Table, { children: [
1062
- /* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
1063
- /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Entity" }),
1064
- /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Reference" }),
1065
- /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Role" })
1066
- ] }) }),
1067
- /* @__PURE__ */ jsx(Table.Body, { children: assetRelations.map((relation) => /* @__PURE__ */ jsxs(Table.Row, { children: [
1068
- /* @__PURE__ */ jsx(Table.Cell, { className: "capitalize", children: relation.entity_type }),
1069
- /* @__PURE__ */ jsx(Table.Cell, { className: "font-mono text-xs", children: relation.entity_id }),
1070
- /* @__PURE__ */ jsx(Table.Cell, { children: relation.relation_role })
1071
- ] }, relation.id)) })
1072
- ] }) : /* @__PURE__ */ jsx("p", { className: "text-sm text-ui-fg-subtle", children: "This asset is not linked to any entities." })
1073
- ] })
1074
- ] }) : /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center text-sm text-ui-fg-subtle", children: "Select an asset to view details." }),
1075
- /* @__PURE__ */ jsxs(FocusModal.Footer, { className: "mt-6 flex flex-wrap items-center justify-between gap-3", children: [
1076
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1077
- /* @__PURE__ */ jsxs(
1132
+ deleteAttempted && asset && /* @__PURE__ */ jsxs("div", { className: "mt-4 rounded-md border border-ui-border-base bg-ui-bg-subtle p-4 text-sm text-ui-fg-subtle", children: [
1133
+ /* @__PURE__ */ jsx("p", { className: "font-medium text-ui-fg-base", children: "Having trouble deleting?" }),
1134
+ /* @__PURE__ */ jsx("p", { className: "mt-1", children: "If the asset is still referenced, remove those relations first or attempt a force delete." }),
1135
+ /* @__PURE__ */ jsx(
1078
1136
  Button,
1079
1137
  {
1080
- type: "button",
1138
+ className: "mt-3",
1081
1139
  variant: "danger",
1140
+ size: "small",
1141
+ disabled: isDeleting,
1142
+ isLoading: isDeleting,
1082
1143
  onClick: () => {
1083
- if (!asset) {
1084
- return;
1085
- }
1086
- const confirmDelete = window.confirm(
1087
- "Delete this asset and all generated variants?"
1144
+ const confirmForce = window.confirm(
1145
+ "Force delete will remove the asset even if it is still referenced. Continue?"
1088
1146
  );
1089
- if (confirmDelete) {
1090
- handleDelete(false);
1147
+ if (confirmForce) {
1148
+ handleDelete(true);
1091
1149
  }
1092
1150
  },
1093
- disabled: !asset,
1094
- children: [
1095
- /* @__PURE__ */ jsx(Trash, { className: "mr-1 size-4" }),
1096
- " Delete"
1097
- ]
1098
- }
1099
- ),
1100
- /* @__PURE__ */ jsxs(
1101
- Button,
1102
- {
1103
- type: "button",
1104
- variant: "secondary",
1105
- onClick: () => asset && handleVariantCopy("medium"),
1106
- disabled: !asset,
1107
- children: [
1108
- /* @__PURE__ */ jsx(ArrowPath, { className: "mr-1 size-4" }),
1109
- " Copy URL"
1110
- ]
1151
+ children: "Force delete"
1111
1152
  }
1112
1153
  )
1154
+ ] })
1155
+ ]
1156
+ }
1157
+ ),
1158
+ /* @__PURE__ */ jsx(
1159
+ Prompt,
1160
+ {
1161
+ variant: "confirmation",
1162
+ open: Boolean(asset) && deletePromptOpen,
1163
+ onOpenChange: (open) => setDeletePromptOpen(open),
1164
+ children: /* @__PURE__ */ jsxs(Prompt.Content, { children: [
1165
+ /* @__PURE__ */ jsxs(Prompt.Header, { children: [
1166
+ /* @__PURE__ */ jsx(Prompt.Title, { children: "Delete asset" }),
1167
+ /* @__PURE__ */ jsx(Prompt.Description, { children: asset ? `Delete "${asset.original_filename}" and all generated variants? This cannot be undone.` : "Delete this asset and all generated variants? This cannot be undone." })
1113
1168
  ] }),
1114
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1115
- /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: onClose, children: "Close" }),
1116
- /* @__PURE__ */ jsx(
1169
+ /* @__PURE__ */ jsxs(Prompt.Footer, { children: [
1170
+ /* @__PURE__ */ jsx(Prompt.Cancel, { children: "Cancel" }),
1171
+ /* @__PURE__ */ jsx(Prompt.Action, { asChild: true, children: /* @__PURE__ */ jsx(
1117
1172
  Button,
1118
1173
  {
1119
- onClick: handleSave,
1120
- disabled: !asset || isSaving,
1121
- isLoading: isSaving,
1122
- children: "Save changes"
1174
+ variant: "danger",
1175
+ onClick: () => handleDelete(false),
1176
+ disabled: isDeleting,
1177
+ isLoading: isDeleting,
1178
+ children: "Delete"
1123
1179
  }
1124
- )
1180
+ ) })
1125
1181
  ] })
1126
- ] }),
1127
- deleteAttempted && asset && /* @__PURE__ */ jsxs("div", { className: "mt-4 rounded-md border border-ui-border-base bg-ui-bg-subtle p-4 text-sm text-ui-fg-subtle", children: [
1128
- /* @__PURE__ */ jsx("p", { className: "font-medium text-ui-fg-base", children: "Having trouble deleting?" }),
1129
- /* @__PURE__ */ jsx("p", { className: "mt-1", children: "If the asset is still referenced, remove those relations first or attempt a force delete." }),
1130
- /* @__PURE__ */ jsx(
1131
- Button,
1132
- {
1133
- className: "mt-3",
1134
- variant: "danger",
1135
- size: "small",
1136
- onClick: () => {
1137
- const confirmForce = window.confirm(
1138
- "Force delete will remove the asset even if it is still referenced. Continue?"
1139
- );
1140
- if (confirmForce) {
1141
- handleDelete(true);
1142
- }
1143
- },
1144
- children: "Force delete"
1145
- }
1146
- )
1147
1182
  ] })
1148
- ]
1149
- }
1150
- ) });
1183
+ }
1184
+ )
1185
+ ] });
1151
1186
  };
1152
1187
  const MediaAssetDrawer = MediaAssetDialog;
1153
1188
  const PAGE_SIZE = 24;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lodashventure/medusa-media-manager",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "description": "Medusa v2 plugin providing a Strapi-inspired media manager with storage adapters, variants, and admin UI",
5
5
  "author": "StandUpCode",
6
6
  "license": "MIT",