@firecms/core 3.0.0-canary.126 → 3.0.0-canary.128

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.
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <h1 align="center">FireCMS</h1>
12
- <h3 align="center">Awesome Firebase/Firestore-based headless CMS</h3>
12
+ <h3 align="center">Awesome Firebase/MongoDB-based headless CMS</h3>
13
13
  <p align="center"><a href="https://demo.firecms.co">Live demo</a></p>
14
14
 
15
15
  <br />
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- export declare function DeleteConfirmationDialog({ open, onAccept, onCancel, title, loading, body }: {
2
+ export declare function ConfirmationDialog({ open, onAccept, onCancel, title, loading, body }: {
3
3
  open: boolean;
4
4
  onAccept: () => void;
5
5
  onCancel: () => void;
@@ -0,0 +1,3 @@
1
+ export declare function addRecentId(collectionId: string, id: string): string[];
2
+ export declare function saveSearchedIdsLocally(collectionId: string, ids: string[]): void;
3
+ export declare function getRecentIds(collectionId: string): string[];
@@ -7,7 +7,7 @@ export type EntityPreviewProps = {
7
7
  collection?: EntityCollection;
8
8
  hover?: boolean;
9
9
  previewProperties?: string[];
10
- disabled: undefined | boolean;
10
+ disabled?: boolean;
11
11
  entity: Entity<any>;
12
12
  includeId?: boolean;
13
13
  includeEntityLink?: boolean;
@@ -18,7 +18,7 @@ export * from "./EntityCollectionTable";
18
18
  export * from "./NotFoundPage";
19
19
  export * from "./VirtualTable";
20
20
  export * from "./ErrorBoundary";
21
- export * from "./DeleteConfirmationDialog";
21
+ export * from "./ConfirmationDialog";
22
22
  export * from "./FireCMSLogo";
23
23
  export * from "../core/DefaultAppBar";
24
24
  export * from "./ArrayContainer";
@@ -5,4 +5,10 @@ export type AccessResponse = {
5
5
  blocked?: boolean;
6
6
  message?: string;
7
7
  };
8
- export declare function useProjectLog(authController: AuthController, dataSourceDelegate: DataSourceDelegate, plugins?: FireCMSPlugin<any, any, any>[]): AccessResponse | null;
8
+ export interface UseProjectLogParams {
9
+ apiKey?: string;
10
+ authController: AuthController;
11
+ dataSourceDelegate: DataSourceDelegate;
12
+ plugins?: FireCMSPlugin<any, any, any>[];
13
+ }
14
+ export declare function useProjectLog({ authController, dataSourceDelegate, plugins, apiKey }: UseProjectLogParams): AccessResponse | null;
package/dist/index.es.js CHANGED
@@ -2960,9 +2960,11 @@ function getArrayValuesCount(array) {
2960
2960
  function makePropertiesEditable(properties) {
2961
2961
  Object.keys(properties).forEach((key) => {
2962
2962
  const property = properties[key];
2963
- property.editable = true;
2964
- if (property.dataType === "map" && property.properties) {
2965
- makePropertiesEditable(property.properties);
2963
+ if (property) {
2964
+ property.editable = true;
2965
+ if (property.dataType === "map" && property.properties) {
2966
+ makePropertiesEditable(property.properties);
2967
+ }
2966
2968
  }
2967
2969
  });
2968
2970
  return properties;
@@ -2970,13 +2972,19 @@ function makePropertiesEditable(properties) {
2970
2972
  function makePropertiesNonEditable(properties) {
2971
2973
  return Object.entries(properties).reduce((acc, [key, property]) => {
2972
2974
  if (!isPropertyBuilder(property) && property.dataType === "map" && property.properties) {
2973
- const updated = { ...property, properties: makePropertiesNonEditable(property.properties) };
2975
+ const updated = {
2976
+ ...property,
2977
+ properties: makePropertiesNonEditable(property.properties)
2978
+ };
2974
2979
  acc[key] = updated;
2975
2980
  }
2976
2981
  if (isPropertyBuilder(property)) {
2977
2982
  acc[key] = property;
2978
2983
  } else {
2979
- acc[key] = { ...property, editable: false };
2984
+ acc[key] = {
2985
+ ...property,
2986
+ editable: false
2987
+ };
2980
2988
  }
2981
2989
  return acc;
2982
2990
  }, {});
@@ -10782,6 +10790,23 @@ function DeleteEntityDialog({
10782
10790
  }
10783
10791
  );
10784
10792
  }
10793
+ function addRecentId(collectionId, id) {
10794
+ const recentIds = getRecentIds(collectionId);
10795
+ const newRecentIds = [id, ...recentIds.filter((i) => i !== id)];
10796
+ if (newRecentIds.length > 5) {
10797
+ newRecentIds.pop();
10798
+ }
10799
+ saveSearchedIdsLocally(collectionId, newRecentIds);
10800
+ return newRecentIds;
10801
+ }
10802
+ function saveSearchedIdsLocally(collectionId, ids) {
10803
+ localStorage.setItem("recent_id_searches::" + collectionId, JSON.stringify(ids));
10804
+ }
10805
+ function getRecentIds(collectionId) {
10806
+ const stored = localStorage.getItem("recent_id_searches::" + collectionId);
10807
+ if (!stored) return [];
10808
+ return JSON.parse(stored);
10809
+ }
10785
10810
  const editEntityAction = {
10786
10811
  icon: /* @__PURE__ */ jsx(KeyboardTabIcon, {}),
10787
10812
  name: "Edit",
@@ -10799,6 +10824,9 @@ const editEntityAction = {
10799
10824
  path: entity.path,
10800
10825
  entityId: entity.id
10801
10826
  });
10827
+ if (collection) {
10828
+ addRecentId(collection.id, entity.id);
10829
+ }
10802
10830
  const path = collection?.collectionGroup ? entity.path : fullPath ?? entity.path;
10803
10831
  context.sideEntityController.open({
10804
10832
  entityId: entity.id,
@@ -12837,6 +12865,7 @@ function EntityIdHeaderWidget({
12837
12865
  }) {
12838
12866
  const [openPopup, setOpenPopup] = React__default.useState(false);
12839
12867
  const [searchString, setSearchString] = React__default.useState("");
12868
+ const [recentIds, setRecentIds] = React__default.useState(getRecentIds(collection.id));
12840
12869
  const sideEntityController = useSideEntityController();
12841
12870
  return /* @__PURE__ */ jsx(Tooltip, { title: !openPopup ? "Find by ID" : void 0, asChild: false, children: /* @__PURE__ */ jsx(
12842
12871
  Popover,
@@ -12847,47 +12876,69 @@ function EntityIdHeaderWidget({
12847
12876
  align: "start",
12848
12877
  alignOffset: -117,
12849
12878
  trigger: /* @__PURE__ */ jsx(IconButton, { size: "small", children: /* @__PURE__ */ jsx(SearchIcon, { size: "small" }) }),
12850
- children: /* @__PURE__ */ jsx(
12851
- "form",
12852
- {
12853
- noValidate: true,
12854
- onSubmit: (e) => {
12855
- e.preventDefault();
12856
- if (!searchString) return;
12857
- setOpenPopup(false);
12858
- return sideEntityController.open({
12859
- entityId: searchString.trim(),
12860
- path,
12861
- collection,
12862
- updateUrl: true
12863
- });
12879
+ children: /* @__PURE__ */ jsxs("div", { className: cls("my-2 rounded-lg bg-gray-50 dark:bg-gray-950 text-gray-900 dark:text-white"), children: [
12880
+ /* @__PURE__ */ jsx(
12881
+ "form",
12882
+ {
12883
+ noValidate: true,
12884
+ onSubmit: (e) => {
12885
+ e.preventDefault();
12886
+ if (!searchString) return;
12887
+ setOpenPopup(false);
12888
+ setRecentIds(addRecentId(collection.id, searchString.trim()));
12889
+ return sideEntityController.open({
12890
+ entityId: searchString.trim(),
12891
+ path,
12892
+ collection,
12893
+ updateUrl: true
12894
+ });
12895
+ },
12896
+ className: "w-96 max-w-full",
12897
+ children: /* @__PURE__ */ jsxs("div", { className: "flex p-2 w-full gap-2", children: [
12898
+ /* @__PURE__ */ jsx(
12899
+ "input",
12900
+ {
12901
+ autoFocus: openPopup,
12902
+ placeholder: "Find entity by ID",
12903
+ onChange: (e) => {
12904
+ setSearchString(e.target.value);
12905
+ },
12906
+ value: searchString,
12907
+ className: "rounded-lg bg-white dark:bg-gray-800 flex-grow bg-transparent outline-none p-2 " + focusedDisabled
12908
+ }
12909
+ ),
12910
+ /* @__PURE__ */ jsx(
12911
+ Button,
12912
+ {
12913
+ variant: "text",
12914
+ disabled: !searchString.trim(),
12915
+ type: "submit",
12916
+ children: /* @__PURE__ */ jsx(KeyboardTabIcon, {})
12917
+ }
12918
+ )
12919
+ ] })
12920
+ }
12921
+ ),
12922
+ recentIds && recentIds.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 p-2", children: recentIds.map((id) => /* @__PURE__ */ jsx(
12923
+ ReferencePreview,
12924
+ {
12925
+ reference: new EntityReference(id, path),
12926
+ hover: true,
12927
+ onClick: () => {
12928
+ setOpenPopup(false);
12929
+ sideEntityController.open({
12930
+ entityId: id,
12931
+ path,
12932
+ collection,
12933
+ updateUrl: true
12934
+ });
12935
+ },
12936
+ includeEntityLink: false,
12937
+ size: "small"
12864
12938
  },
12865
- className: "text-gray-900 dark:text-white w-96 max-w-full",
12866
- children: /* @__PURE__ */ jsxs("div", { className: "flex p-2 w-full gap-4", children: [
12867
- /* @__PURE__ */ jsx(
12868
- "input",
12869
- {
12870
- autoFocus: openPopup,
12871
- placeholder: "Find entity by ID",
12872
- onChange: (e) => {
12873
- setSearchString(e.target.value);
12874
- },
12875
- value: searchString,
12876
- className: "flex-grow bg-transparent outline-none p-1 " + focusedDisabled
12877
- }
12878
- ),
12879
- /* @__PURE__ */ jsx(
12880
- Button,
12881
- {
12882
- variant: "outlined",
12883
- disabled: !searchString.trim(),
12884
- type: "submit",
12885
- children: "Go"
12886
- }
12887
- )
12888
- ] })
12889
- }
12890
- )
12939
+ id
12940
+ )) })
12941
+ ] })
12891
12942
  }
12892
12943
  ) });
12893
12944
  }
@@ -12982,7 +13033,7 @@ function NotFoundPage() {
12982
13033
  }
12983
13034
  ) });
12984
13035
  }
12985
- function DeleteConfirmationDialog({
13036
+ function ConfirmationDialog({
12986
13037
  open,
12987
13038
  onAccept,
12988
13039
  onCancel,
@@ -13517,7 +13568,7 @@ function ArrayItemOptions({
13517
13568
  return /* @__PURE__ */ jsx(
13518
13569
  "div",
13519
13570
  {
13520
- className: `pl-2 pt-1 pb-4 flex ${direction === "row" ? "flex-row-reverse" : "flex-col"} items-center`,
13571
+ className: `pl-2 pt-1 pb-1 flex ${direction === "row" ? "flex-row-reverse" : "flex-col"} items-center`,
13521
13572
  ref: iconRef,
13522
13573
  ...provided.dragHandleProps,
13523
13574
  children: /* @__PURE__ */ jsxs(
@@ -19005,7 +19056,7 @@ function useBuildDataSource({
19005
19056
  };
19006
19057
  }
19007
19058
  const DEFAULT_SERVER = "https://api-drplyi3b6q-ey.a.run.app";
19008
- async function makeRequest(authController, dataSourceKey, pluginKeys) {
19059
+ async function makeRequest(authController, dataSourceKey, pluginKeys, apiKey) {
19009
19060
  let idToken;
19010
19061
  try {
19011
19062
  idToken = await authController.getAuthToken();
@@ -19022,6 +19073,7 @@ async function makeRequest(authController, dataSourceKey, pluginKeys) {
19022
19073
  Authorization: `Basic ${idToken}`
19023
19074
  },
19024
19075
  body: JSON.stringify({
19076
+ apiKey,
19025
19077
  email: authController.user?.email ?? null,
19026
19078
  datasource: dataSourceKey,
19027
19079
  plugins: pluginKeys
@@ -19031,14 +19083,19 @@ async function makeRequest(authController, dataSourceKey, pluginKeys) {
19031
19083
  return res.json();
19032
19084
  });
19033
19085
  }
19034
- function useProjectLog(authController, dataSourceDelegate, plugins) {
19086
+ function useProjectLog({
19087
+ authController,
19088
+ dataSourceDelegate,
19089
+ plugins,
19090
+ apiKey
19091
+ }) {
19035
19092
  const [accessResponse, setAccessResponse] = useState(null);
19036
19093
  const accessedUserRef = useRef(null);
19037
19094
  const dataSourceKey = dataSourceDelegate.key;
19038
19095
  const pluginKeys = plugins?.map((plugin) => plugin.key);
19039
19096
  useEffect(() => {
19040
19097
  if (authController.user && authController.user.uid !== accessedUserRef.current && !authController.initialLoading) {
19041
- makeRequest(authController, dataSourceKey, pluginKeys).then(setAccessResponse);
19098
+ makeRequest(authController, dataSourceKey, pluginKeys, apiKey).then(setAccessResponse);
19042
19099
  accessedUserRef.current = authController.user.uid;
19043
19100
  }
19044
19101
  }, [authController, dataSourceKey, pluginKeys]);
@@ -19059,7 +19116,8 @@ function FireCMS(props) {
19059
19116
  propertyConfigs,
19060
19117
  entityViews,
19061
19118
  components,
19062
- navigationController
19119
+ navigationController,
19120
+ apiKey
19063
19121
  } = props;
19064
19122
  useLocaleConfig(locale);
19065
19123
  const dataSource = useBuildDataSource({
@@ -19083,7 +19141,15 @@ function FireCMS(props) {
19083
19141
  const analyticsController = useMemo(() => ({
19084
19142
  onAnalyticsEvent
19085
19143
  }), []);
19086
- const accessResponse = useProjectLog(authController, dataSourceDelegate, plugins);
19144
+ const accessResponse = useProjectLog({
19145
+ apiKey,
19146
+ authController,
19147
+ dataSourceDelegate,
19148
+ plugins
19149
+ });
19150
+ if (accessResponse?.message) {
19151
+ console.warn(accessResponse.message);
19152
+ }
19087
19153
  if (navigationController.navigationLoadingError) {
19088
19154
  return /* @__PURE__ */ jsx(CenteredView, { maxWidth: "md", children: /* @__PURE__ */ jsx(
19089
19155
  ErrorView,
@@ -19103,10 +19169,10 @@ function FireCMS(props) {
19103
19169
  ) });
19104
19170
  }
19105
19171
  if (accessResponse?.blocked) {
19106
- return /* @__PURE__ */ jsxs(CenteredView, { maxWidth: "md", fullScreen: true, children: [
19107
- /* @__PURE__ */ jsx(Typography, { variant: "h4", children: "Access blocked" }),
19172
+ return /* @__PURE__ */ jsxs(CenteredView, { maxWidth: "md", fullScreen: true, className: "flex flex-col gap-2", children: [
19173
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", gutterBottom: true, children: "License needed" }),
19108
19174
  /* @__PURE__ */ jsxs(Typography, { children: [
19109
- "This app has been blocked. Please reach out at ",
19175
+ "You need a valid license to use FireCMS PRO. Please reach out at ",
19110
19176
  /* @__PURE__ */ jsx(
19111
19177
  "a",
19112
19178
  {
@@ -19116,10 +19182,7 @@ function FireCMS(props) {
19116
19182
  ),
19117
19183
  " for more information."
19118
19184
  ] }),
19119
- accessResponse?.message && /* @__PURE__ */ jsxs(Typography, { children: [
19120
- "Response from the server: ",
19121
- accessResponse?.message
19122
- ] })
19185
+ accessResponse?.message && /* @__PURE__ */ jsx(Typography, { children: accessResponse?.message })
19123
19186
  ] });
19124
19187
  }
19125
19188
  return /* @__PURE__ */ jsx(AnalyticsContext.Provider, { value: analyticsController, children: /* @__PURE__ */ jsx(CustomizationControllerContext.Provider, { value: customizationController, children: /* @__PURE__ */ jsx(
@@ -19752,6 +19815,7 @@ export {
19752
19815
  BooleanPreview,
19753
19816
  COLLECTION_PATH_SEPARATOR,
19754
19817
  CircularProgressCenter,
19818
+ ConfirmationDialog,
19755
19819
  DEFAULT_FIELD_CONFIGS,
19756
19820
  DRAWER_WIDTH,
19757
19821
  DatePreview,
@@ -19759,7 +19823,6 @@ export {
19759
19823
  DefaultAppBar,
19760
19824
  DefaultDrawer,
19761
19825
  DefaultHomePage,
19762
- DeleteConfirmationDialog,
19763
19826
  Drawer,
19764
19827
  DrawerLogo,
19765
19828
  DrawerNavigationItem,