@firecms/entity_history 3.0.1 → 3.1.0-canary.768c91f

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.
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Fetches the latest history entry from the __history subcollection
3
+ * and displays who last edited the entity and when.
4
+ */
5
+ export declare function LastEditedByIndicator({ path, entityId, collection }: {
6
+ path: string;
7
+ entityId: string;
8
+ collection: any;
9
+ }): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,6 @@
1
+ import { PluginFormActionProps } from "@firecms/core";
2
+ /**
3
+ * Renders the "last edited by" indicator in the entity form top bar.
4
+ * Used as a plugin `form.ActionsTop` component.
5
+ */
6
+ export declare function LastEditedByFormAction({ entityId, path, status, collection, }: PluginFormActionProps): import("react/jsx-runtime").JSX.Element | null;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./useEntityHistoryPlugin";
2
2
  export * from "./HistoryControllerProvider";
3
3
  export { createHistoryEntry } from "./entity_history_callbacks";
4
+ export { LastEditedByIndicator } from "./components/LastEditedByIndicator";
4
5
  export * from "./types";
package/dist/index.es.js CHANGED
@@ -3,7 +3,7 @@ import * as React from "react";
3
3
  import React__default, { useContext, useState, useRef, useEffect, useCallback, useMemo } from "react";
4
4
  import { useAuthController, useCustomizationController, useNavigationController, useSideEntityController, resolveCollection, getPropertyInPath, getValueInPath, PropertyPreview, SkeletonPropertyComponent, useSnackbarController, useDataSource, EntityView, ErrorBoundary, ConfirmationDialog, mergeCallbacks } from "@firecms/core";
5
5
  import { c } from "react-compiler-runtime";
6
- import { Chip, Tooltip, Typography, cls, IconButton, KeyboardTabIcon, defaultBorderMixin, KeyboardBackspaceIcon, Label, HistoryIcon } from "@firecms/ui";
6
+ import { Chip, Tooltip, Typography, IconButton, KeyboardTabIcon, cls, defaultBorderMixin, KeyboardBackspaceIcon, Label, HistoryIcon } from "@firecms/ui";
7
7
  import equal from "react-fast-compare";
8
8
  const HistoryControllerContext = React__default.createContext({});
9
9
  const useHistoryController = () => {
@@ -325,7 +325,7 @@ function EntityHistoryView(t0) {
325
325
  const handleObserver = (entries) => {
326
326
  const target = entries[0];
327
327
  if (target.isIntersecting && hasMore && !isLoading) {
328
- setLimit(_temp);
328
+ setLimit(_temp$1);
329
329
  }
330
330
  };
331
331
  const observer = new IntersectionObserver(handleObserver, options);
@@ -571,7 +571,7 @@ function EntityHistoryView(t0) {
571
571
  }
572
572
  return t19;
573
573
  }
574
- function _temp(prev) {
574
+ function _temp$1(prev) {
575
575
  return prev + 5;
576
576
  }
577
577
  function createHistoryEntry({
@@ -661,6 +661,159 @@ function findChangedFields(oldValues, newValues, prefix = "") {
661
661
  }
662
662
  return changedFields;
663
663
  }
664
+ function getRelativeTimeString(date) {
665
+ const now = /* @__PURE__ */ new Date();
666
+ const diffMs = now.getTime() - date.getTime();
667
+ const diffSeconds = Math.floor(diffMs / 1e3);
668
+ const diffMinutes = Math.floor(diffSeconds / 60);
669
+ const diffHours = Math.floor(diffMinutes / 60);
670
+ const diffDays = Math.floor(diffHours / 24);
671
+ if (diffSeconds < 60) return "just now";
672
+ if (diffMinutes < 60) return `${diffMinutes}m ago`;
673
+ if (diffHours < 24) return `${diffHours}h ago`;
674
+ if (diffDays < 30) return `${diffDays}d ago`;
675
+ return date.toLocaleDateString();
676
+ }
677
+ function LastEditedByIndicator(t0) {
678
+ const $ = c(21);
679
+ const {
680
+ path,
681
+ entityId,
682
+ collection
683
+ } = t0;
684
+ const {
685
+ getUser
686
+ } = useHistoryController();
687
+ const dataSource = useDataSource();
688
+ const [latestEntry, setLatestEntry] = useState();
689
+ let t1;
690
+ if ($[0] !== collection || $[1] !== dataSource || $[2] !== entityId || $[3] !== path) {
691
+ t1 = () => {
692
+ if (!path || !entityId) {
693
+ return;
694
+ }
695
+ const historyPath = `${path}/${entityId}/__history`;
696
+ const unsubscribe = dataSource.listenCollection?.({
697
+ path: historyPath,
698
+ collection,
699
+ orderBy: "__metadata.updated_on",
700
+ order: "desc",
701
+ limit: 1,
702
+ onUpdate: (entities) => {
703
+ setLatestEntry(entities[0]);
704
+ },
705
+ onError: _temp
706
+ });
707
+ return () => {
708
+ if (typeof unsubscribe === "function") {
709
+ unsubscribe();
710
+ }
711
+ };
712
+ };
713
+ $[0] = collection;
714
+ $[1] = dataSource;
715
+ $[2] = entityId;
716
+ $[3] = path;
717
+ $[4] = t1;
718
+ } else {
719
+ t1 = $[4];
720
+ }
721
+ let t2;
722
+ if ($[5] !== dataSource || $[6] !== entityId || $[7] !== path) {
723
+ t2 = [path, entityId, dataSource];
724
+ $[5] = dataSource;
725
+ $[6] = entityId;
726
+ $[7] = path;
727
+ $[8] = t2;
728
+ } else {
729
+ t2 = $[8];
730
+ }
731
+ useEffect(t1, t2);
732
+ const metadata = latestEntry?.values?.__metadata;
733
+ const uid = metadata?.updated_by;
734
+ const editedOn = metadata?.updated_on;
735
+ if (!uid && !editedOn) {
736
+ return null;
737
+ }
738
+ let t3;
739
+ if ($[9] !== getUser || $[10] !== uid) {
740
+ t3 = uid ? getUser?.(uid) : void 0;
741
+ $[9] = getUser;
742
+ $[10] = uid;
743
+ $[11] = t3;
744
+ } else {
745
+ t3 = $[11];
746
+ }
747
+ const user = t3;
748
+ const date = editedOn instanceof Date ? editedOn : editedOn?.toDate ? editedOn.toDate() : null;
749
+ const timeString = date ? getRelativeTimeString(date) : null;
750
+ const displayName = user?.displayName ?? user?.email ?? uid;
751
+ const photoURL = user?.photoURL;
752
+ let t4;
753
+ if ($[12] !== displayName || $[13] !== photoURL) {
754
+ t4 = photoURL ? /* @__PURE__ */ jsx("img", { src: photoURL, alt: displayName ?? "User", className: "rounded-full object-cover w-6 h-6" }) : /* @__PURE__ */ jsx("div", { className: "rounded-full bg-primary/10 dark:bg-primary-dark/20 flex items-center justify-center text-primary dark:text-primary-dark font-medium w-6 h-6 text-xs", children: (displayName ?? "?").charAt(0).toUpperCase() });
755
+ $[12] = displayName;
756
+ $[13] = photoURL;
757
+ $[14] = t4;
758
+ } else {
759
+ t4 = $[14];
760
+ }
761
+ const t5 = timeString ? ` · ${timeString}` : "";
762
+ let t6;
763
+ if ($[15] !== displayName || $[16] !== t5) {
764
+ t6 = /* @__PURE__ */ jsxs("span", { children: [
765
+ displayName,
766
+ t5
767
+ ] });
768
+ $[15] = displayName;
769
+ $[16] = t5;
770
+ $[17] = t6;
771
+ } else {
772
+ t6 = $[17];
773
+ }
774
+ let t7;
775
+ if ($[18] !== t4 || $[19] !== t6) {
776
+ t7 = /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-text-secondary dark:text-text-secondary-dark", children: [
777
+ t4,
778
+ t6
779
+ ] });
780
+ $[18] = t4;
781
+ $[19] = t6;
782
+ $[20] = t7;
783
+ } else {
784
+ t7 = $[20];
785
+ }
786
+ return t7;
787
+ }
788
+ function _temp(error) {
789
+ console.error("Error fetching latest history entry:", error);
790
+ }
791
+ function LastEditedByFormAction(t0) {
792
+ const $ = c(4);
793
+ const {
794
+ entityId,
795
+ path,
796
+ status,
797
+ collection
798
+ } = t0;
799
+ if (status === "new" || status === "copy" || !entityId) {
800
+ return null;
801
+ }
802
+ if (!collection.history) {
803
+ return null;
804
+ }
805
+ let t1;
806
+ if ($[0] !== collection || $[1] !== entityId || $[2] !== path) {
807
+ t1 = /* @__PURE__ */ jsx(LastEditedByIndicator, { path, entityId, collection });
808
+ $[0] = collection;
809
+ $[1] = entityId;
810
+ $[2] = path;
811
+ $[3] = t1;
812
+ } else {
813
+ t1 = $[3];
814
+ }
815
+ return t1;
816
+ }
664
817
  function useEntityHistoryPlugin(props) {
665
818
  const {
666
819
  defaultEnabled = false
@@ -669,6 +822,7 @@ function useEntityHistoryPlugin(props) {
669
822
  if (collection.history === true || defaultEnabled && collection.history !== false) {
670
823
  return {
671
824
  ...collection,
825
+ history: true,
672
826
  entityViews: [...collection.entityViews ?? [], {
673
827
  key: "__history",
674
828
  name: "History",
@@ -689,6 +843,9 @@ function useEntityHistoryPlugin(props) {
689
843
  getUser: props?.getUser
690
844
  }
691
845
  },
846
+ form: {
847
+ BeforeTitle: LastEditedByFormAction
848
+ },
692
849
  collection: {
693
850
  modifyCollection
694
851
  }
@@ -697,6 +854,7 @@ function useEntityHistoryPlugin(props) {
697
854
  export {
698
855
  HistoryControllerContext,
699
856
  HistoryControllerProvider,
857
+ LastEditedByIndicator,
700
858
  createHistoryEntry,
701
859
  useEntityHistoryPlugin,
702
860
  useHistoryController
@@ -1 +1 @@
1
- {"version":3,"file":"index.es.js","sources":["../src/HistoryControllerProvider.tsx","../src/components/UserChip.tsx","../src/components/EntityHistoryEntry.tsx","../src/components/EntityHistoryView.tsx","../src/entity_history_callbacks.ts","../src/useEntityHistoryPlugin.tsx"],"sourcesContent":["import React, { PropsWithChildren, useContext } from \"react\";\nimport equal from \"react-fast-compare\"\n\nimport { User } from \"@firecms/core\";\n\nexport type HistoryConfigController = {\n /**\n * Function to get a user by uid.\n * @param uid\n */\n getUser?: (uid: string) => User | null;\n}\n\nexport const HistoryControllerContext = React.createContext<HistoryConfigController>({} as any);\nexport const useHistoryController = (): HistoryConfigController => useContext(HistoryControllerContext);\n\n\nexport interface HistoryControllerProviderProps {\n\n getUser?: (uid: string) => User | null;\n\n}\n\nexport const HistoryControllerProvider = React.memo(\n function HistoryControllerProvider({\n children,\n getUser,\n }: PropsWithChildren<HistoryControllerProviderProps>) {\n\n return (\n <HistoryControllerContext.Provider\n value={{\n getUser,\n }}>\n\n {children}\n\n </HistoryControllerContext.Provider>\n );\n }, equal);\n","import { User } from \"@firecms/core\";\nimport { Chip, Tooltip } from \"@firecms/ui\";\n\nexport function UserChip({ user }: { user: User }) {\n return (\n <Tooltip title={user.email ?? user.uid}>\n <Chip size={\"small\"} className={\"flex items-center\"}>\n {user.photoURL && <img\n className={\"rounded-full w-6 h-6 mr-2\"}\n src={user.photoURL} alt={user.displayName ?? \"User picture\"}/>}\n <span>{user.displayName ?? user.email ?? user.uid}</span>\n </Chip>\n </Tooltip>\n );\n}\n","import * as React from \"react\";\n\nimport {\n Chip,\n cls,\n defaultBorderMixin,\n DescriptionIcon,\n IconButton, KeyboardBackspaceIcon,\n KeyboardTabIcon,\n Tooltip,\n Typography\n} from \"@firecms/ui\";\nimport {\n Entity,\n EntityCollection,\n EntityValues,\n getPropertyInPath,\n getValueInPath,\n PreviewSize,\n Property,\n PropertyPreview,\n resolveCollection,\n ResolvedProperty,\n SkeletonPropertyComponent,\n useAuthController,\n useCustomizationController,\n useNavigationController,\n useSideEntityController\n} from \"@firecms/core\";\nimport { useHistoryController } from \"../HistoryControllerProvider\";\nimport { UserChip } from \"./UserChip\";\n\nexport type EntityPreviewProps = {\n size: PreviewSize,\n actions?: React.ReactNode,\n collection?: EntityCollection,\n hover?: boolean;\n previewKeys?: string[],\n entity: Entity<any>,\n previousValues?: EntityValues<any>;\n onClick?: (e: React.SyntheticEvent) => void;\n};\n\nfunction PreviousValueView({\n previousValueInPath,\n childProperty,\n propertyKey\n }: {\n previousValueInPath: any,\n childProperty: Property,\n propertyKey: string\n}) {\n if (typeof previousValueInPath === \"string\" || typeof previousValueInPath === \"number\") {\n return <Typography variant={\"caption\"} color={\"secondary\"} className=\"line-through\">\n {previousValueInPath}\n </Typography>;\n } else if (typeof previousValueInPath === \"boolean\") {\n return <Typography variant={\"caption\"} color={\"secondary\"} className=\"line-through\">\n {previousValueInPath ? \"true\" : \"false\"}\n </Typography>;\n\n } else {\n return <Tooltip\n side={\"left\"}\n title={<div className={\"flex flex-col gap-2\"}>\n <Typography variant={\"caption\"} color={\"secondary\"}>\n Previous value\n </Typography>\n <PropertyPreview\n propertyKey={propertyKey as string}\n value={previousValueInPath}\n property={childProperty as ResolvedProperty}\n size={\"small\"}/>\n </div>}>\n <KeyboardBackspaceIcon size={\"smallest\"} color={\"disabled\"} className={\"mb-1\"}/>\n </Tooltip>\n }\n}\n\n/**\n * This view is used to display a preview of an entity.\n * It is used by default in reference fields and whenever a reference is displayed.\n */\nexport function EntityHistoryEntry({\n actions,\n hover,\n collection: collectionProp,\n previewKeys,\n onClick,\n size,\n entity,\n previousValues\n }: EntityPreviewProps) {\n\n const authController = useAuthController();\n const customizationController = useCustomizationController();\n\n const navigationController = useNavigationController();\n const sideEntityController = useSideEntityController();\n\n const collection = collectionProp ?? navigationController.getCollection(entity.path);\n const updatedOn = entity.values?.[\"__metadata\"]?.[\"updated_on\"];\n if (!collection) {\n throw Error(`Couldn't find the corresponding collection view for the path: ${entity.path}`);\n }\n\n const updatedBy = entity.values?.[\"__metadata\"]?.[\"updated_by\"];\n const { getUser } = useHistoryController();\n const user = getUser?.(updatedBy);\n\n const resolvedCollection = React.useMemo(() => resolveCollection({\n collection,\n path: entity.path,\n values: entity.values,\n propertyConfigs: customizationController.propertyConfigs,\n authController\n }), [collection]);\n\n return <div className={\"w-full flex flex-col gap-2 mt-4\"}>\n <div className={\"ml-4 flex items-center gap-4\"}>\n <Typography variant={\"body2\"} color={\"secondary\"}>{updatedOn.toLocaleString()}</Typography>\n {!user && updatedBy && <Chip size={\"small\"}>{updatedBy}</Chip>}\n {user && <UserChip user={user}/>}\n </div>\n <div\n className={cls(\n \"bg-white dark:bg-surface-900\",\n \"min-h-[44px]\",\n \"w-full\",\n \"items-center\",\n hover ? \"hover:bg-surface-accent-50 dark:hover:bg-surface-800 group-hover:bg-surface-accent-50 dark:group-hover:bg-surface-800\" : \"\",\n size === \"small\" ? \"p-1\" : \"px-2 py-1\",\n \"flex border rounded-lg\",\n onClick ? \"cursor-pointer\" : \"\",\n defaultBorderMixin\n )}>\n\n\n {actions}\n\n {entity &&\n <Tooltip title={\"See details for this revision\"}\n className={\"my-2 grow-0 shrink-0 self-start\"}>\n <IconButton\n color={\"inherit\"}\n className={\"\"}\n onClick={(e) => {\n\n sideEntityController.open({\n entityId: entity.id,\n path: entity.path,\n allowFullScreen: false,\n collection: {\n ...collection,\n subcollections: undefined,\n entityViews: undefined,\n permissions: {\n create: false,\n delete: false,\n edit: false,\n read: true\n }\n },\n updateUrl: true\n });\n }}>\n <KeyboardTabIcon/>\n </IconButton>\n </Tooltip>}\n\n <div className={\"flex flex-col grow w-full m-1 shrink min-w-0\"}>\n\n {previewKeys && previewKeys.map((key) => {\n const childProperty = getPropertyInPath(resolvedCollection.properties, key);\n\n const valueInPath = getValueInPath(entity.values, key);\n const previousValueInPath = previousValues ? getValueInPath(previousValues, key) : undefined;\n\n const element = childProperty ? (entity\n ? <PropertyPreview\n propertyKey={key as string}\n value={valueInPath}\n property={childProperty as ResolvedProperty}\n size={\"small\"}/>\n : <SkeletonPropertyComponent\n property={childProperty as ResolvedProperty}\n size={\"small\"}/>) :\n <Typography variant={\"body2\"}>\n {typeof valueInPath === \"string\" ? valueInPath : JSON.stringify(valueInPath)}\n </Typography>;\n return (\n <div key={\"ref_prev_\" + key}\n className=\"flex w-full my-1 items-center\">\n <Typography variant={\"caption\"}\n color={\"secondary\"}\n className=\"min-w-[140px] md:min-w-[200px] w-1/5 pr-8 overflow-hidden text-ellipsis text-right\">\n {key}\n </Typography>\n <div className=\"w-4/5\">\n {previousValueInPath !== undefined && previousValueInPath !== valueInPath &&\n <PreviousValueView previousValueInPath={previousValueInPath}\n childProperty={childProperty as ResolvedProperty}\n propertyKey={key}/>\n }\n {element}\n </div>\n </div>\n );\n })}\n\n </div>\n\n </div>\n </div>\n}\n\n","import { useEffect, useRef, useState } from \"react\";\nimport {\n ConfirmationDialog,\n Entity,\n EntityCustomViewParams,\n EntityView,\n ErrorBoundary,\n useAuthController,\n useDataSource,\n useSnackbarController\n} from \"@firecms/core\";\nimport { cls, HistoryIcon, IconButton, Label, Tooltip, Typography } from \"@firecms/ui\";\nimport { EntityHistoryEntry } from \"./EntityHistoryEntry\";\n\nexport function EntityHistoryView({\n entity,\n collection,\n formContext\n }: EntityCustomViewParams) {\n\n const authController = useAuthController();\n const snackbarController = useSnackbarController();\n const dirty = formContext?.formex.dirty;\n\n const dataSource = useDataSource();\n const pathAndId = entity ? entity?.path + \"/\" + entity?.id : undefined;\n\n const [revertVersionDialog, setRevertVersionDialog] = useState<Entity | undefined>(undefined);\n const [revisions, setRevisions] = useState<Entity[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [hasMore, setHasMore] = useState(true);\n\n const PAGE_SIZE = 5;\n const [limit, setLimit] = useState(PAGE_SIZE);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const observerRef = useRef<IntersectionObserver | null>(null);\n const loadMoreRef = useRef<HTMLDivElement>(null);\n\n // Load revisions with the current limit\n useEffect(() => {\n if (!pathAndId) return;\n\n setIsLoading(true); // Set loading true when fetching starts\n const listener = dataSource.listenCollection?.({\n path: pathAndId + \"/__history\",\n collection: collection,\n order: \"desc\",\n orderBy: \"__metadata.updated_on\",\n limit: limit,\n startAfter: undefined,\n onUpdate: (entities) => {\n setRevisions(entities);\n setHasMore(entities.length === limit && entities.length >= PAGE_SIZE); // Ensure we fetched a full page to consider hasMore\n setIsLoading(false);\n },\n onError: (error) => {\n console.error(\"Error fetching history:\", error);\n setIsLoading(false);\n setHasMore(false); // Stop trying if there's an error\n }\n });\n return () => {\n if (typeof listener === \"function\") {\n listener();\n }\n };\n }, [pathAndId, limit, dataSource]);\n\n // Setup intersection observer for infinite scroll\n useEffect(() => {\n const currentContainer = containerRef.current;\n const currentLoadMore = loadMoreRef.current;\n\n // Conditions for active observation\n if (!currentContainer || !currentLoadMore || !hasMore || isLoading) {\n // If we shouldn't be observing, ensure any existing observer is disconnected\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n return;\n }\n\n // Options for the IntersectionObserver\n const options = {\n root: currentContainer,\n rootMargin: \"0px 0px 200px 0px\", // Trigger 200px before the sentinel is at the bottom edge\n threshold: 0.01 // Trigger if even a small part is visible within the rootMargin\n };\n\n // The callback for when the sentinel's intersection state changes\n const handleObserver = (entries: IntersectionObserverEntry[]) => {\n const target = entries[0];\n if (target.isIntersecting && hasMore && !isLoading) {\n // No need to setIsLoading(true) here, it's done in the data fetching useEffect\n setLimit(prev => prev + PAGE_SIZE);\n }\n };\n\n const observer = new IntersectionObserver(handleObserver, options);\n observer.observe(currentLoadMore);\n observerRef.current = observer; // Store the new observer\n\n // Cleanup function for this effect instance\n return () => {\n observer.disconnect(); // Disconnect the observer created in *this* effect run\n if (observerRef.current === observer) {\n observerRef.current = null;\n }\n };\n // Re-run if hasMore, isLoading changes, or if revisions.length changes (which might make loadMoreRef available/unavailable)\n }, [hasMore, isLoading, revisions.length]);\n\n if (!entity) {\n return <div className=\"flex items-center justify-center h-full\">\n <Label>History is only available for existing entities</Label>\n </div>\n }\n\n function doRevert(revertVersion: Entity) {\n if (!entity) {\n throw new Error(\"No entity to revert\");\n }\n const revertValues = {\n ...revertVersion.values,\n __metadata: {\n ...revertVersion.values?.[\"__metadata\"],\n reverted: true,\n updated_on: new Date(),\n updated_by: authController.user?.uid ?? null,\n }\n };\n const saveReverted = dataSource.saveEntity({\n path: entity.path,\n entityId: entity.id,\n values: revertValues,\n collection,\n status: \"existing\"\n });\n const saveRevertedHistory = dataSource.saveEntity({\n path: revertVersion.path,\n entityId: revertVersion.id,\n values: revertValues,\n collection,\n status: \"existing\"\n });\n return Promise.all([saveReverted, saveRevertedHistory])\n .then(() => {\n formContext.formex.resetForm({\n values: revertVersion.values\n });\n setRevertVersionDialog(undefined);\n snackbarController.open({\n message: \"Reverted version\",\n type: \"info\"\n });\n }\n ).catch((error) => {\n console.error(\"Error reverting entity:\", error);\n snackbarController.open({\n message: \"Error reverting entity\",\n type: \"error\"\n });\n });\n\n }\n\n return <div\n ref={containerRef}\n className={cls(\"relative flex-1 h-full overflow-auto w-full flex flex-col gap-4 p-8\")}>\n <div className=\"flex flex-col gap-2 max-w-6xl mx-auto w-full\">\n\n <Typography variant={\"h5\"} className={\"mt-24 ml-4\"}>\n History\n </Typography>\n\n {revisions.length === 0 && <>\n <Label className={\"ml-4 mt-8\"}>\n No history available\n </Label>\n <Typography variant={\"caption\"} className={\"ml-4\"}>\n When you save an entity, a new version is created and stored in the history.\n </Typography>\n </>}\n\n {revisions.map((revision, index) => {\n const previewKeys = revision.values?.[\"__metadata\"]?.[\"changed_fields\"];\n const previousValues: object | undefined = revision.values?.[\"__metadata\"]?.[\"previous_values\"];\n return <div key={index} className=\"flex flex-cols gap-2 w-full\">\n <EntityHistoryEntry size={\"large\"}\n entity={revision}\n collection={collection}\n previewKeys={previewKeys}\n previousValues={previousValues}\n actions={\n <Tooltip title={\"Revert to this version\"}\n className={\"m-2 grow-0 self-start\"}>\n <IconButton\n onClick={() => {\n if (dirty) {\n snackbarController.open({\n message: \"Please save or discard your changes before reverting\",\n type: \"warning\"\n });\n } else {\n setRevertVersionDialog(revision);\n }\n }}>\n <HistoryIcon/>\n </IconButton>\n </Tooltip>}\n />\n </div>\n })}\n\n {/* Load more sentinel element */}\n {revisions.length > 0 && (\n <div\n ref={loadMoreRef}\n className=\"py-4 text-center\"\n >\n {isLoading && <Label>Loading more...</Label>}\n {!hasMore && revisions.length > PAGE_SIZE && <Label>No more history available</Label>}\n </div>\n )}\n </div>\n\n <ErrorBoundary>\n <ConfirmationDialog open={Boolean(revertVersionDialog)}\n onAccept={function (): void {\n if (!revertVersionDialog) return;\n doRevert(revertVersionDialog);\n }}\n onCancel={function (): void {\n setRevertVersionDialog(undefined);\n }}\n title={<Typography variant={\"subtitle2\"}>Revert data to this version?</Typography>}\n body={revertVersionDialog ?\n <EntityView entity={revertVersionDialog}\n collection={collection}\n path={entity?.path}/> : null}/>\n </ErrorBoundary>\n </div>\n}\n","import { EntityCallbacks, FireCMSContext, User } from \"@firecms/core\";\nimport equal from \"react-fast-compare\"\nimport { HistoryEntry, NewHistoryEntryParams } from \"./types\";\n\n\n\nexport function createHistoryEntry<T = any>({\n context,\n previousValues,\n values,\n path,\n entityId,\n collection\n}: NewHistoryEntryParams<T>) {\n\n const uid = context.authController.user?.uid;\n const dataSource = context.dataSource;\n const changedFields = previousValues ? findChangedFields(previousValues as object, values as object) : null;\n\n const entry: HistoryEntry<T> = {\n ...values,\n __metadata: {\n previous_values: previousValues,\n changed_fields: changedFields,\n updated_on: new Date(),\n updated_by: uid ?? null,\n }\n };\n dataSource.saveEntity({\n path: path + \"/\" + entityId + \"/__history\",\n values: entry,\n status: \"new\",\n collection\n }).then(() => {\n console.debug(\"History saved for\", path, entityId);\n });\n}\n\nexport const entityHistoryCallbacks: EntityCallbacks = {\n onSaveSuccess: async (props) => {\n\n const values = props.values;\n const previousValues = props.previousValues;\n const path = props.path;\n const entityId = props.entityId;\n const context = props.context;\n const collection = props.collection;\n createHistoryEntry({\n context: context,\n previousValues: previousValues,\n values: values,\n path: path,\n entityId: entityId,\n collection: collection\n });\n }\n}\n\nfunction findChangedFields<M extends object>(oldValues: M, newValues: M, prefix: string = \"\"): string[] {\n const changedFields: string[] = [];\n\n // Handle null/undefined cases\n if (equal(oldValues, newValues)) return changedFields;\n if (!oldValues || !newValues) return [prefix || \".\"];\n\n // Get all unique keys from both objects\n const allKeys = new Set([\n ...Object.keys(oldValues),\n ...Object.keys(newValues)\n ]);\n\n for (const key of allKeys) {\n const oldValue = oldValues[key as keyof M];\n const newValue = newValues[key as keyof M];\n const currentPath = prefix ? `${prefix}.${key}` : key;\n\n // If key exists only in one object\n if ((key in oldValues) !== (key in newValues)) {\n changedFields.push(currentPath);\n continue;\n }\n\n // If values are identical (deep equality)\n if (equal(oldValue, newValue)) continue;\n\n // Handle arrays\n if (Array.isArray(oldValue) && Array.isArray(newValue)) {\n if (oldValue.length !== newValue.length) {\n changedFields.push(currentPath);\n } else {\n // Check if any array element changed\n for (let i = 0; i < oldValue.length; i++) {\n if (\n typeof oldValue[i] === \"object\" && oldValue[i] !== null &&\n typeof newValue[i] === \"object\" && newValue[i] !== null\n ) {\n const nestedChanges = findChangedFields(\n oldValue[i] as object,\n newValue[i] as object,\n `${currentPath}[${i}]`\n );\n if (nestedChanges.length > 0) {\n changedFields.push(currentPath);\n break;\n }\n } else if (!equal(oldValue[i], newValue[i])) {\n changedFields.push(currentPath);\n break;\n }\n }\n }\n }\n // Handle nested objects\n else if (\n typeof oldValue === \"object\" && oldValue !== null &&\n typeof newValue === \"object\" && newValue !== null\n ) {\n const nestedChanges = findChangedFields(\n oldValue as object,\n newValue as object,\n currentPath\n );\n changedFields.push(...nestedChanges);\n }\n // Handle primitives\n else {\n changedFields.push(currentPath);\n }\n }\n\n return changedFields;\n}\n","import { useCallback, useMemo } from \"react\";\nimport { EntityCollection, FireCMSPlugin, mergeCallbacks, User } from \"@firecms/core\";\nimport { EntityHistoryView } from \"./components/EntityHistoryView\";\nimport { HistoryIcon } from \"@firecms/ui\";\nimport { entityHistoryCallbacks } from \"./entity_history_callbacks\";\nimport { HistoryControllerProvider } from \"./HistoryControllerProvider\";\n\n/**\n * This plugin adds a history view to the entity side panel.\n */\nexport function useEntityHistoryPlugin(props?: EntityHistoryPluginProps): FireCMSPlugin<any, any, any, EntityHistoryPluginProps> {\n\n const { defaultEnabled = false } = props ?? {};\n\n const modifyCollection = useCallback((collection: EntityCollection) => {\n if (collection.history === true || (defaultEnabled && collection.history !== false)) {\n return {\n ...collection,\n entityViews: [\n ...(collection.entityViews ?? []),\n {\n key: \"__history\",\n name: \"History\",\n tabComponent: <HistoryIcon size={\"small\"}/>,\n Builder: EntityHistoryView,\n position: \"start\"\n }\n ],\n callbacks: mergeCallbacks(collection.callbacks, entityHistoryCallbacks)\n } satisfies EntityCollection;\n }\n return collection;\n }, []);\n\n return useMemo(() => ({\n key: \"entity_history\",\n provider: {\n Component: HistoryControllerProvider,\n props: {\n getUser: props?.getUser\n }\n },\n collection: {\n modifyCollection\n }\n } satisfies FireCMSPlugin), [props]);\n}\n\nexport type EntityHistoryPluginProps = {\n /**\n * If true, the history view will be enabled to all collections by default.\n * Each collection can override this value by setting the `history` property.\n */\n defaultEnabled?: boolean;\n\n /**\n * Function to get the user object from the uid.\n * @param uid\n */\n getUser?: (uid: string) => User | null;\n}\n"],"names":["HistoryControllerContext","React","createContext","useHistoryController","useContext","HistoryControllerProvider","memo","t0","$","_c","children","getUser","t1","t2","equal","UserChip","user","email","uid","displayName","photoURL","t3","t4","t5","t6","PreviousValueView","previousValueInPath","childProperty","propertyKey","Symbol","for","EntityHistoryEntry","actions","hover","collection","collectionProp","previewKeys","onClick","size","entity","previousValues","authController","useAuthController","customizationController","useCustomizationController","navigationController","useNavigationController","sideEntityController","useSideEntityController","getCollection","path","updatedOn","values","Error","updatedBy","resolvedCollection","useMemo","resolveCollection","propertyConfigs","toLocaleString","cls","defaultBorderMixin","e","open","entityId","id","allowFullScreen","subcollections","undefined","entityViews","permissions","create","delete","edit","read","updateUrl","map","key","getPropertyInPath","properties","valueInPath","getValueInPath","element","JSON","stringify","EntityHistoryView","formContext","snackbarController","useSnackbarController","dirty","formex","dataSource","useDataSource","pathAndId","revertVersionDialog","setRevertVersionDialog","useState","revisions","setRevisions","isLoading","setIsLoading","hasMore","setHasMore","limit","setLimit","containerRef","useRef","observerRef","loadMoreRef","listener","listenCollection","order","orderBy","startAfter","onUpdate","entities","length","onError","error","console","useEffect","currentContainer","current","currentLoadMore","disconnect","options","root","rootMargin","threshold","handleObserver","entries","target","isIntersecting","_temp","observer","IntersectionObserver","observe","doRevert","revertVersion","revertValues","__metadata","reverted","updated_on","Date","updated_by","saveReverted","saveEntity","status","saveRevertedHistory","Promise","all","then","resetForm","message","type","catch","error_0","t7","t8","t9","t10","t11","revision","index","changed_fields","previous_values","t12","t13","Boolean","t14","t15","t16","t17","t18","t19","prev","createHistoryEntry","context","changedFields","findChangedFields","entry","debug","entityHistoryCallbacks","onSaveSuccess","props","oldValues","newValues","prefix","allKeys","Set","Object","keys","oldValue","newValue","currentPath","push","Array","isArray","i","nestedChanges","useEntityHistoryPlugin","defaultEnabled","modifyCollection","useCallback","history","name","tabComponent","Builder","position","callbacks","mergeCallbacks","provider","Component"],"mappings":"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"}
1
+ {"version":3,"file":"index.es.js","sources":["../src/HistoryControllerProvider.tsx","../src/components/UserChip.tsx","../src/components/EntityHistoryEntry.tsx","../src/components/EntityHistoryView.tsx","../src/entity_history_callbacks.ts","../src/components/LastEditedByIndicator.tsx","../src/components/LastEditedByPluginComponents.tsx","../src/useEntityHistoryPlugin.tsx"],"sourcesContent":["import React, { PropsWithChildren, useContext } from \"react\";\nimport equal from \"react-fast-compare\"\n\nimport { User } from \"@firecms/core\";\n\nexport type HistoryConfigController = {\n /**\n * Function to get a user by uid.\n * @param uid\n */\n getUser?: (uid: string) => User | null;\n}\n\nexport const HistoryControllerContext = React.createContext<HistoryConfigController>({} as any);\nexport const useHistoryController = (): HistoryConfigController => useContext(HistoryControllerContext);\n\n\nexport interface HistoryControllerProviderProps {\n\n getUser?: (uid: string) => User | null;\n\n}\n\nexport const HistoryControllerProvider = React.memo(\n function HistoryControllerProvider({\n children,\n getUser,\n }: PropsWithChildren<HistoryControllerProviderProps>) {\n\n return (\n <HistoryControllerContext.Provider\n value={{\n getUser,\n }}>\n\n {children}\n\n </HistoryControllerContext.Provider>\n );\n }, equal);\n","import { User } from \"@firecms/core\";\nimport { Chip, Tooltip } from \"@firecms/ui\";\n\nexport function UserChip({ user }: { user: User }) {\n return (\n <Tooltip title={user.email ?? user.uid}>\n <Chip size={\"small\"} className={\"flex items-center\"}>\n {user.photoURL && <img\n className={\"rounded-full w-6 h-6 mr-2\"}\n src={user.photoURL} alt={user.displayName ?? \"User picture\"}/>}\n <span>{user.displayName ?? user.email ?? user.uid}</span>\n </Chip>\n </Tooltip>\n );\n}\n","import * as React from \"react\";\n\nimport {\n Chip,\n cls,\n defaultBorderMixin,\n DescriptionIcon,\n IconButton, KeyboardBackspaceIcon,\n KeyboardTabIcon,\n Tooltip,\n Typography\n} from \"@firecms/ui\";\nimport {\n Entity,\n EntityCollection,\n EntityValues,\n getPropertyInPath,\n getValueInPath,\n PreviewSize,\n Property,\n PropertyPreview,\n resolveCollection,\n ResolvedProperty,\n SkeletonPropertyComponent,\n useAuthController,\n useCustomizationController,\n useNavigationController,\n useSideEntityController\n} from \"@firecms/core\";\nimport { useHistoryController } from \"../HistoryControllerProvider\";\nimport { UserChip } from \"./UserChip\";\n\nexport type EntityPreviewProps = {\n size: PreviewSize,\n actions?: React.ReactNode,\n collection?: EntityCollection,\n hover?: boolean;\n previewKeys?: string[],\n entity: Entity<any>,\n previousValues?: EntityValues<any>;\n onClick?: (e: React.SyntheticEvent) => void;\n};\n\nfunction PreviousValueView({\n previousValueInPath,\n childProperty,\n propertyKey\n }: {\n previousValueInPath: any,\n childProperty: Property,\n propertyKey: string\n}) {\n if (typeof previousValueInPath === \"string\" || typeof previousValueInPath === \"number\") {\n return <Typography variant={\"caption\"} color={\"secondary\"} className=\"line-through\">\n {previousValueInPath}\n </Typography>;\n } else if (typeof previousValueInPath === \"boolean\") {\n return <Typography variant={\"caption\"} color={\"secondary\"} className=\"line-through\">\n {previousValueInPath ? \"true\" : \"false\"}\n </Typography>;\n\n } else {\n return <Tooltip\n side={\"left\"}\n title={<div className={\"flex flex-col gap-2\"}>\n <Typography variant={\"caption\"} color={\"secondary\"}>\n Previous value\n </Typography>\n <PropertyPreview\n propertyKey={propertyKey as string}\n value={previousValueInPath}\n property={childProperty as ResolvedProperty}\n size={\"small\"}/>\n </div>}>\n <KeyboardBackspaceIcon size={\"smallest\"} color={\"disabled\"} className={\"mb-1\"}/>\n </Tooltip>\n }\n}\n\n/**\n * This view is used to display a preview of an entity.\n * It is used by default in reference fields and whenever a reference is displayed.\n */\nexport function EntityHistoryEntry({\n actions,\n hover,\n collection: collectionProp,\n previewKeys,\n onClick,\n size,\n entity,\n previousValues\n }: EntityPreviewProps) {\n\n const authController = useAuthController();\n const customizationController = useCustomizationController();\n\n const navigationController = useNavigationController();\n const sideEntityController = useSideEntityController();\n\n const collection = collectionProp ?? navigationController.getCollection(entity.path);\n const updatedOn = entity.values?.[\"__metadata\"]?.[\"updated_on\"];\n if (!collection) {\n throw Error(`Couldn't find the corresponding collection view for the path: ${entity.path}`);\n }\n\n const updatedBy = entity.values?.[\"__metadata\"]?.[\"updated_by\"];\n const { getUser } = useHistoryController();\n const user = getUser?.(updatedBy);\n\n const resolvedCollection = React.useMemo(() => resolveCollection({\n collection,\n path: entity.path,\n values: entity.values,\n propertyConfigs: customizationController.propertyConfigs,\n authController\n }), [collection]);\n\n return <div className={\"w-full flex flex-col gap-2 mt-4\"}>\n <div className={\"ml-4 flex items-center gap-4\"}>\n <Typography variant={\"body2\"} color={\"secondary\"}>{updatedOn.toLocaleString()}</Typography>\n {!user && updatedBy && <Chip size={\"small\"}>{updatedBy}</Chip>}\n {user && <UserChip user={user}/>}\n </div>\n <div\n className={cls(\n \"bg-white dark:bg-surface-900\",\n \"min-h-[44px]\",\n \"w-full\",\n \"items-center\",\n hover ? \"hover:bg-surface-accent-50 dark:hover:bg-surface-800 group-hover:bg-surface-accent-50 dark:group-hover:bg-surface-800\" : \"\",\n size === \"small\" ? \"p-1\" : \"px-2 py-1\",\n \"flex border rounded-lg\",\n onClick ? \"cursor-pointer\" : \"\",\n defaultBorderMixin\n )}>\n\n\n {actions}\n\n {entity &&\n <Tooltip title={\"See details for this revision\"}\n className={\"my-2 grow-0 shrink-0 self-start\"}>\n <IconButton\n color={\"inherit\"}\n className={\"\"}\n onClick={(e) => {\n\n sideEntityController.open({\n entityId: entity.id,\n path: entity.path,\n allowFullScreen: false,\n collection: {\n ...collection,\n subcollections: undefined,\n entityViews: undefined,\n permissions: {\n create: false,\n delete: false,\n edit: false,\n read: true\n }\n },\n updateUrl: true\n });\n }}>\n <KeyboardTabIcon/>\n </IconButton>\n </Tooltip>}\n\n <div className={\"flex flex-col grow w-full m-1 shrink min-w-0\"}>\n\n {previewKeys && previewKeys.map((key) => {\n const childProperty = getPropertyInPath(resolvedCollection.properties, key);\n\n const valueInPath = getValueInPath(entity.values, key);\n const previousValueInPath = previousValues ? getValueInPath(previousValues, key) : undefined;\n\n const element = childProperty ? (entity\n ? <PropertyPreview\n propertyKey={key as string}\n value={valueInPath}\n property={childProperty as ResolvedProperty}\n size={\"small\"}/>\n : <SkeletonPropertyComponent\n property={childProperty as ResolvedProperty}\n size={\"small\"}/>) :\n <Typography variant={\"body2\"}>\n {typeof valueInPath === \"string\" ? valueInPath : JSON.stringify(valueInPath)}\n </Typography>;\n return (\n <div key={\"ref_prev_\" + key}\n className=\"flex w-full my-1 items-center\">\n <Typography variant={\"caption\"}\n color={\"secondary\"}\n className=\"min-w-[140px] md:min-w-[200px] w-1/5 pr-8 overflow-hidden text-ellipsis text-right\">\n {key}\n </Typography>\n <div className=\"w-4/5\">\n {previousValueInPath !== undefined && previousValueInPath !== valueInPath &&\n <PreviousValueView previousValueInPath={previousValueInPath}\n childProperty={childProperty as ResolvedProperty}\n propertyKey={key}/>\n }\n {element}\n </div>\n </div>\n );\n })}\n\n </div>\n\n </div>\n </div>\n}\n\n","import { useEffect, useRef, useState } from \"react\";\nimport {\n ConfirmationDialog,\n Entity,\n EntityCustomViewParams,\n EntityView,\n ErrorBoundary,\n useAuthController,\n useDataSource,\n useSnackbarController\n} from \"@firecms/core\";\nimport { cls, HistoryIcon, IconButton, Label, Tooltip, Typography } from \"@firecms/ui\";\nimport { EntityHistoryEntry } from \"./EntityHistoryEntry\";\n\nexport function EntityHistoryView({\n entity,\n collection,\n formContext\n }: EntityCustomViewParams) {\n\n const authController = useAuthController();\n const snackbarController = useSnackbarController();\n const dirty = formContext?.formex.dirty;\n\n const dataSource = useDataSource();\n const pathAndId = entity ? entity?.path + \"/\" + entity?.id : undefined;\n\n const [revertVersionDialog, setRevertVersionDialog] = useState<Entity | undefined>(undefined);\n const [revisions, setRevisions] = useState<Entity[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [hasMore, setHasMore] = useState(true);\n\n const PAGE_SIZE = 5;\n const [limit, setLimit] = useState(PAGE_SIZE);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const observerRef = useRef<IntersectionObserver | null>(null);\n const loadMoreRef = useRef<HTMLDivElement>(null);\n\n // Load revisions with the current limit\n useEffect(() => {\n if (!pathAndId) return;\n\n setIsLoading(true); // Set loading true when fetching starts\n const listener = dataSource.listenCollection?.({\n path: pathAndId + \"/__history\",\n collection: collection,\n order: \"desc\",\n orderBy: \"__metadata.updated_on\",\n limit: limit,\n startAfter: undefined,\n onUpdate: (entities) => {\n setRevisions(entities);\n setHasMore(entities.length === limit && entities.length >= PAGE_SIZE); // Ensure we fetched a full page to consider hasMore\n setIsLoading(false);\n },\n onError: (error) => {\n console.error(\"Error fetching history:\", error);\n setIsLoading(false);\n setHasMore(false); // Stop trying if there's an error\n }\n });\n return () => {\n if (typeof listener === \"function\") {\n listener();\n }\n };\n }, [pathAndId, limit, dataSource]);\n\n // Setup intersection observer for infinite scroll\n useEffect(() => {\n const currentContainer = containerRef.current;\n const currentLoadMore = loadMoreRef.current;\n\n // Conditions for active observation\n if (!currentContainer || !currentLoadMore || !hasMore || isLoading) {\n // If we shouldn't be observing, ensure any existing observer is disconnected\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n return;\n }\n\n // Options for the IntersectionObserver\n const options = {\n root: currentContainer,\n rootMargin: \"0px 0px 200px 0px\", // Trigger 200px before the sentinel is at the bottom edge\n threshold: 0.01 // Trigger if even a small part is visible within the rootMargin\n };\n\n // The callback for when the sentinel's intersection state changes\n const handleObserver = (entries: IntersectionObserverEntry[]) => {\n const target = entries[0];\n if (target.isIntersecting && hasMore && !isLoading) {\n // No need to setIsLoading(true) here, it's done in the data fetching useEffect\n setLimit(prev => prev + PAGE_SIZE);\n }\n };\n\n const observer = new IntersectionObserver(handleObserver, options);\n observer.observe(currentLoadMore);\n observerRef.current = observer; // Store the new observer\n\n // Cleanup function for this effect instance\n return () => {\n observer.disconnect(); // Disconnect the observer created in *this* effect run\n if (observerRef.current === observer) {\n observerRef.current = null;\n }\n };\n // Re-run if hasMore, isLoading changes, or if revisions.length changes (which might make loadMoreRef available/unavailable)\n }, [hasMore, isLoading, revisions.length]);\n\n if (!entity) {\n return <div className=\"flex items-center justify-center h-full\">\n <Label>History is only available for existing entities</Label>\n </div>\n }\n\n function doRevert(revertVersion: Entity) {\n if (!entity) {\n throw new Error(\"No entity to revert\");\n }\n const revertValues = {\n ...revertVersion.values,\n __metadata: {\n ...revertVersion.values?.[\"__metadata\"],\n reverted: true,\n updated_on: new Date(),\n updated_by: authController.user?.uid ?? null,\n }\n };\n const saveReverted = dataSource.saveEntity({\n path: entity.path,\n entityId: entity.id,\n values: revertValues,\n collection,\n status: \"existing\"\n });\n const saveRevertedHistory = dataSource.saveEntity({\n path: revertVersion.path,\n entityId: revertVersion.id,\n values: revertValues,\n collection,\n status: \"existing\"\n });\n return Promise.all([saveReverted, saveRevertedHistory])\n .then(() => {\n formContext.formex.resetForm({\n values: revertVersion.values\n });\n setRevertVersionDialog(undefined);\n snackbarController.open({\n message: \"Reverted version\",\n type: \"info\"\n });\n }\n ).catch((error) => {\n console.error(\"Error reverting entity:\", error);\n snackbarController.open({\n message: \"Error reverting entity\",\n type: \"error\"\n });\n });\n\n }\n\n return <div\n ref={containerRef}\n className={cls(\"relative flex-1 h-full overflow-auto w-full flex flex-col gap-4 p-8\")}>\n <div className=\"flex flex-col gap-2 max-w-6xl mx-auto w-full\">\n\n <Typography variant={\"h5\"} className={\"mt-24 ml-4\"}>\n History\n </Typography>\n\n {revisions.length === 0 && <>\n <Label className={\"ml-4 mt-8\"}>\n No history available\n </Label>\n <Typography variant={\"caption\"} className={\"ml-4\"}>\n When you save an entity, a new version is created and stored in the history.\n </Typography>\n </>}\n\n {revisions.map((revision, index) => {\n const previewKeys = revision.values?.[\"__metadata\"]?.[\"changed_fields\"];\n const previousValues: object | undefined = revision.values?.[\"__metadata\"]?.[\"previous_values\"];\n return <div key={index} className=\"flex flex-cols gap-2 w-full\">\n <EntityHistoryEntry size={\"large\"}\n entity={revision}\n collection={collection}\n previewKeys={previewKeys}\n previousValues={previousValues}\n actions={\n <Tooltip title={\"Revert to this version\"}\n className={\"m-2 grow-0 self-start\"}>\n <IconButton\n onClick={() => {\n if (dirty) {\n snackbarController.open({\n message: \"Please save or discard your changes before reverting\",\n type: \"warning\"\n });\n } else {\n setRevertVersionDialog(revision);\n }\n }}>\n <HistoryIcon/>\n </IconButton>\n </Tooltip>}\n />\n </div>\n })}\n\n {/* Load more sentinel element */}\n {revisions.length > 0 && (\n <div\n ref={loadMoreRef}\n className=\"py-4 text-center\"\n >\n {isLoading && <Label>Loading more...</Label>}\n {!hasMore && revisions.length > PAGE_SIZE && <Label>No more history available</Label>}\n </div>\n )}\n </div>\n\n <ErrorBoundary>\n <ConfirmationDialog open={Boolean(revertVersionDialog)}\n onAccept={function (): void {\n if (!revertVersionDialog) return;\n doRevert(revertVersionDialog);\n }}\n onCancel={function (): void {\n setRevertVersionDialog(undefined);\n }}\n title={<Typography variant={\"subtitle2\"}>Revert data to this version?</Typography>}\n body={revertVersionDialog ?\n <EntityView entity={revertVersionDialog}\n collection={collection}\n path={entity?.path}/> : null}/>\n </ErrorBoundary>\n </div>\n}\n","import { EntityCallbacks, FireCMSContext, User } from \"@firecms/core\";\nimport equal from \"react-fast-compare\"\nimport { HistoryEntry, NewHistoryEntryParams } from \"./types\";\n\n\n\nexport function createHistoryEntry<T = any>({\n context,\n previousValues,\n values,\n path,\n entityId,\n collection\n}: NewHistoryEntryParams<T>) {\n\n const uid = context.authController.user?.uid;\n const dataSource = context.dataSource;\n const changedFields = previousValues ? findChangedFields(previousValues as object, values as object) : null;\n\n const entry: HistoryEntry<T> = {\n ...values,\n __metadata: {\n previous_values: previousValues,\n changed_fields: changedFields,\n updated_on: new Date(),\n updated_by: uid ?? null,\n }\n };\n dataSource.saveEntity({\n path: path + \"/\" + entityId + \"/__history\",\n values: entry,\n status: \"new\",\n collection\n }).then(() => {\n console.debug(\"History saved for\", path, entityId);\n });\n}\n\nexport const entityHistoryCallbacks: EntityCallbacks = {\n onSaveSuccess: async (props) => {\n\n const values = props.values;\n const previousValues = props.previousValues;\n const path = props.path;\n const entityId = props.entityId;\n const context = props.context;\n const collection = props.collection;\n createHistoryEntry({\n context: context,\n previousValues: previousValues,\n values: values,\n path: path,\n entityId: entityId,\n collection: collection\n });\n }\n}\n\nfunction findChangedFields<M extends object>(oldValues: M, newValues: M, prefix: string = \"\"): string[] {\n const changedFields: string[] = [];\n\n // Handle null/undefined cases\n if (equal(oldValues, newValues)) return changedFields;\n if (!oldValues || !newValues) return [prefix || \".\"];\n\n // Get all unique keys from both objects\n const allKeys = new Set([\n ...Object.keys(oldValues),\n ...Object.keys(newValues)\n ]);\n\n for (const key of allKeys) {\n const oldValue = oldValues[key as keyof M];\n const newValue = newValues[key as keyof M];\n const currentPath = prefix ? `${prefix}.${key}` : key;\n\n // If key exists only in one object\n if ((key in oldValues) !== (key in newValues)) {\n changedFields.push(currentPath);\n continue;\n }\n\n // If values are identical (deep equality)\n if (equal(oldValue, newValue)) continue;\n\n // Handle arrays\n if (Array.isArray(oldValue) && Array.isArray(newValue)) {\n if (oldValue.length !== newValue.length) {\n changedFields.push(currentPath);\n } else {\n // Check if any array element changed\n for (let i = 0; i < oldValue.length; i++) {\n if (\n typeof oldValue[i] === \"object\" && oldValue[i] !== null &&\n typeof newValue[i] === \"object\" && newValue[i] !== null\n ) {\n const nestedChanges = findChangedFields(\n oldValue[i] as object,\n newValue[i] as object,\n `${currentPath}[${i}]`\n );\n if (nestedChanges.length > 0) {\n changedFields.push(currentPath);\n break;\n }\n } else if (!equal(oldValue[i], newValue[i])) {\n changedFields.push(currentPath);\n break;\n }\n }\n }\n }\n // Handle nested objects\n else if (\n typeof oldValue === \"object\" && oldValue !== null &&\n typeof newValue === \"object\" && newValue !== null\n ) {\n const nestedChanges = findChangedFields(\n oldValue as object,\n newValue as object,\n currentPath\n );\n changedFields.push(...nestedChanges);\n }\n // Handle primitives\n else {\n changedFields.push(currentPath);\n }\n }\n\n return changedFields;\n}\n","import React, { useEffect, useState } from \"react\";\nimport { Entity, useDataSource, User } from \"@firecms/core\";\nimport { useHistoryController } from \"../HistoryControllerProvider\";\n\nfunction getRelativeTimeString(date: Date): string {\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffSeconds = Math.floor(diffMs / 1000);\n const diffMinutes = Math.floor(diffSeconds / 60);\n const diffHours = Math.floor(diffMinutes / 60);\n const diffDays = Math.floor(diffHours / 24);\n\n if (diffSeconds < 60) return \"just now\";\n if (diffMinutes < 60) return `${diffMinutes}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays < 30) return `${diffDays}d ago`;\n return date.toLocaleDateString();\n}\n\n/**\n * Fetches the latest history entry from the __history subcollection\n * and displays who last edited the entity and when.\n */\nexport function LastEditedByIndicator({\n path,\n entityId,\n collection\n}: {\n path: string;\n entityId: string;\n collection: any;\n}) {\n const { getUser } = useHistoryController();\n const dataSource = useDataSource();\n const [latestEntry, setLatestEntry] = useState<Entity | undefined>();\n\n useEffect(() => {\n if (!path || !entityId) return;\n\n const historyPath = `${path}/${entityId}/__history`;\n const unsubscribe = dataSource.listenCollection?.({\n path: historyPath,\n collection,\n orderBy: \"__metadata.updated_on\",\n order: \"desc\",\n limit: 1,\n onUpdate: (entities) => {\n setLatestEntry(entities[0]);\n },\n onError: (error) => {\n console.error(\"Error fetching latest history entry:\", error);\n }\n });\n\n return () => {\n if (typeof unsubscribe === \"function\") {\n unsubscribe();\n }\n };\n }, [path, entityId, dataSource]);\n\n const metadata = latestEntry?.values?.__metadata;\n const uid = metadata?.updated_by;\n const editedOn = metadata?.updated_on;\n\n if (!uid && !editedOn) return null;\n\n const user: User | null | undefined = uid ? getUser?.(uid) : undefined;\n const date = editedOn instanceof Date ? editedOn : (editedOn?.toDate ? editedOn.toDate() : null);\n const timeString = date ? getRelativeTimeString(date) : null;\n\n const displayName = user?.displayName ?? user?.email ?? uid;\n const photoURL = user?.photoURL;\n\n return (\n <div className=\"flex items-center gap-2 text-xs text-text-secondary dark:text-text-secondary-dark\">\n {photoURL ? (\n <img\n src={photoURL}\n alt={displayName ?? \"User\"}\n className=\"rounded-full object-cover w-6 h-6\"\n />\n ) : (\n <div className=\"rounded-full bg-primary/10 dark:bg-primary-dark/20 flex items-center justify-center text-primary dark:text-primary-dark font-medium w-6 h-6 text-xs\">\n {(displayName ?? \"?\").charAt(0).toUpperCase()}\n </div>\n )}\n <span>\n {displayName}{timeString ? ` · ${timeString}` : \"\"}\n </span>\n </div>\n );\n}\n","import React from \"react\";\nimport { PluginFormActionProps } from \"@firecms/core\";\nimport { LastEditedByIndicator } from \"./LastEditedByIndicator\";\n\n/**\n * Renders the \"last edited by\" indicator in the entity form top bar.\n * Used as a plugin `form.ActionsTop` component.\n */\nexport function LastEditedByFormAction({\n entityId,\n path,\n status,\n collection,\n}: PluginFormActionProps) {\n if (status === \"new\" || status === \"copy\" || !entityId) return null;\n if (!collection.history) return null;\n\n return <LastEditedByIndicator\n path={path}\n entityId={entityId}\n collection={collection}\n />;\n}\n","import { useCallback, useMemo } from \"react\";\nimport { EntityCollection, FireCMSPlugin, mergeCallbacks, User } from \"@firecms/core\";\nimport { EntityHistoryView } from \"./components/EntityHistoryView\";\nimport { HistoryIcon } from \"@firecms/ui\";\nimport { entityHistoryCallbacks } from \"./entity_history_callbacks\";\nimport { HistoryControllerProvider } from \"./HistoryControllerProvider\";\nimport { LastEditedByFormAction } from \"./components/LastEditedByPluginComponents\";\n\n/**\n * This plugin adds a history view to the entity side panel.\n */\nexport function useEntityHistoryPlugin(props?: EntityHistoryPluginProps): FireCMSPlugin<any, any, any, EntityHistoryPluginProps> {\n\n const { defaultEnabled = false } = props ?? {};\n\n const modifyCollection = useCallback((collection: EntityCollection) => {\n if (collection.history === true || (defaultEnabled && collection.history !== false)) {\n return {\n ...collection,\n history: true,\n entityViews: [\n ...(collection.entityViews ?? []),\n {\n key: \"__history\",\n name: \"History\",\n tabComponent: <HistoryIcon size={\"small\"} />,\n Builder: EntityHistoryView,\n position: \"start\"\n }\n ],\n callbacks: mergeCallbacks(collection.callbacks, entityHistoryCallbacks)\n } satisfies EntityCollection;\n }\n return collection;\n }, []);\n\n return useMemo(() => ({\n key: \"entity_history\",\n provider: {\n Component: HistoryControllerProvider,\n props: {\n getUser: props?.getUser\n }\n },\n form: {\n BeforeTitle: LastEditedByFormAction\n },\n collection: {\n modifyCollection\n }\n } satisfies FireCMSPlugin), [props]);\n}\n\nexport type EntityHistoryPluginProps = {\n /**\n * If true, the history view will be enabled to all collections by default.\n * Each collection can override this value by setting the `history` property.\n */\n defaultEnabled?: boolean;\n\n /**\n * Function to get the user object from the uid.\n * @param uid\n */\n getUser?: (uid: string) => User | null;\n}\n"],"names":["HistoryControllerContext","React","createContext","useHistoryController","useContext","HistoryControllerProvider","memo","t0","$","_c","children","getUser","t1","t2","equal","UserChip","user","email","uid","displayName","photoURL","t3","t4","t5","t6","PreviousValueView","previousValueInPath","childProperty","propertyKey","Symbol","for","EntityHistoryEntry","actions","hover","collection","collectionProp","previewKeys","onClick","size","entity","previousValues","authController","useAuthController","customizationController","useCustomizationController","navigationController","useNavigationController","sideEntityController","useSideEntityController","getCollection","path","updatedOn","values","Error","updatedBy","resolvedCollection","useMemo","resolveCollection","propertyConfigs","toLocaleString","cls","defaultBorderMixin","e","open","entityId","id","allowFullScreen","subcollections","undefined","entityViews","permissions","create","delete","edit","read","updateUrl","map","key","getPropertyInPath","properties","valueInPath","getValueInPath","element","JSON","stringify","EntityHistoryView","formContext","snackbarController","useSnackbarController","dirty","formex","dataSource","useDataSource","pathAndId","revertVersionDialog","setRevertVersionDialog","useState","revisions","setRevisions","isLoading","setIsLoading","hasMore","setHasMore","limit","setLimit","containerRef","useRef","observerRef","loadMoreRef","listener","listenCollection","order","orderBy","startAfter","onUpdate","entities","length","onError","error","console","useEffect","currentContainer","current","currentLoadMore","disconnect","options","root","rootMargin","threshold","handleObserver","entries","target","isIntersecting","_temp","observer","IntersectionObserver","observe","doRevert","revertVersion","revertValues","__metadata","reverted","updated_on","Date","updated_by","saveReverted","saveEntity","status","saveRevertedHistory","Promise","all","then","resetForm","message","type","catch","error_0","t7","t8","t9","t10","t11","revision","index","changed_fields","previous_values","t12","t13","Boolean","t14","t15","t16","t17","t18","t19","prev","createHistoryEntry","context","changedFields","findChangedFields","entry","debug","entityHistoryCallbacks","onSaveSuccess","props","oldValues","newValues","prefix","allKeys","Set","Object","keys","oldValue","newValue","currentPath","push","Array","isArray","i","nestedChanges","getRelativeTimeString","date","now","diffMs","getTime","diffSeconds","Math","floor","diffMinutes","diffHours","diffDays","toLocaleDateString","LastEditedByIndicator","latestEntry","setLatestEntry","historyPath","unsubscribe","metadata","editedOn","toDate","timeString","charAt","toUpperCase","LastEditedByFormAction","history","useEntityHistoryPlugin","defaultEnabled","modifyCollection","useCallback","name","tabComponent","Builder","position","callbacks","mergeCallbacks","provider","Component","form","BeforeTitle"],"mappings":"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"}
package/dist/index.umd.js CHANGED
@@ -339,7 +339,7 @@
339
339
  const handleObserver = (entries) => {
340
340
  const target = entries[0];
341
341
  if (target.isIntersecting && hasMore && !isLoading) {
342
- setLimit(_temp);
342
+ setLimit(_temp$1);
343
343
  }
344
344
  };
345
345
  const observer = new IntersectionObserver(handleObserver, options);
@@ -585,7 +585,7 @@
585
585
  }
586
586
  return t19;
587
587
  }
588
- function _temp(prev) {
588
+ function _temp$1(prev) {
589
589
  return prev + 5;
590
590
  }
591
591
  function createHistoryEntry({
@@ -675,6 +675,159 @@
675
675
  }
676
676
  return changedFields;
677
677
  }
678
+ function getRelativeTimeString(date) {
679
+ const now = /* @__PURE__ */ new Date();
680
+ const diffMs = now.getTime() - date.getTime();
681
+ const diffSeconds = Math.floor(diffMs / 1e3);
682
+ const diffMinutes = Math.floor(diffSeconds / 60);
683
+ const diffHours = Math.floor(diffMinutes / 60);
684
+ const diffDays = Math.floor(diffHours / 24);
685
+ if (diffSeconds < 60) return "just now";
686
+ if (diffMinutes < 60) return `${diffMinutes}m ago`;
687
+ if (diffHours < 24) return `${diffHours}h ago`;
688
+ if (diffDays < 30) return `${diffDays}d ago`;
689
+ return date.toLocaleDateString();
690
+ }
691
+ function LastEditedByIndicator(t0) {
692
+ const $ = reactCompilerRuntime.c(21);
693
+ const {
694
+ path,
695
+ entityId,
696
+ collection
697
+ } = t0;
698
+ const {
699
+ getUser
700
+ } = useHistoryController();
701
+ const dataSource = core.useDataSource();
702
+ const [latestEntry, setLatestEntry] = React.useState();
703
+ let t1;
704
+ if ($[0] !== collection || $[1] !== dataSource || $[2] !== entityId || $[3] !== path) {
705
+ t1 = () => {
706
+ if (!path || !entityId) {
707
+ return;
708
+ }
709
+ const historyPath = `${path}/${entityId}/__history`;
710
+ const unsubscribe = dataSource.listenCollection?.({
711
+ path: historyPath,
712
+ collection,
713
+ orderBy: "__metadata.updated_on",
714
+ order: "desc",
715
+ limit: 1,
716
+ onUpdate: (entities) => {
717
+ setLatestEntry(entities[0]);
718
+ },
719
+ onError: _temp
720
+ });
721
+ return () => {
722
+ if (typeof unsubscribe === "function") {
723
+ unsubscribe();
724
+ }
725
+ };
726
+ };
727
+ $[0] = collection;
728
+ $[1] = dataSource;
729
+ $[2] = entityId;
730
+ $[3] = path;
731
+ $[4] = t1;
732
+ } else {
733
+ t1 = $[4];
734
+ }
735
+ let t2;
736
+ if ($[5] !== dataSource || $[6] !== entityId || $[7] !== path) {
737
+ t2 = [path, entityId, dataSource];
738
+ $[5] = dataSource;
739
+ $[6] = entityId;
740
+ $[7] = path;
741
+ $[8] = t2;
742
+ } else {
743
+ t2 = $[8];
744
+ }
745
+ React.useEffect(t1, t2);
746
+ const metadata = latestEntry?.values?.__metadata;
747
+ const uid = metadata?.updated_by;
748
+ const editedOn = metadata?.updated_on;
749
+ if (!uid && !editedOn) {
750
+ return null;
751
+ }
752
+ let t3;
753
+ if ($[9] !== getUser || $[10] !== uid) {
754
+ t3 = uid ? getUser?.(uid) : void 0;
755
+ $[9] = getUser;
756
+ $[10] = uid;
757
+ $[11] = t3;
758
+ } else {
759
+ t3 = $[11];
760
+ }
761
+ const user = t3;
762
+ const date = editedOn instanceof Date ? editedOn : editedOn?.toDate ? editedOn.toDate() : null;
763
+ const timeString = date ? getRelativeTimeString(date) : null;
764
+ const displayName = user?.displayName ?? user?.email ?? uid;
765
+ const photoURL = user?.photoURL;
766
+ let t4;
767
+ if ($[12] !== displayName || $[13] !== photoURL) {
768
+ t4 = photoURL ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: photoURL, alt: displayName ?? "User", className: "rounded-full object-cover w-6 h-6" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-full bg-primary/10 dark:bg-primary-dark/20 flex items-center justify-center text-primary dark:text-primary-dark font-medium w-6 h-6 text-xs", children: (displayName ?? "?").charAt(0).toUpperCase() });
769
+ $[12] = displayName;
770
+ $[13] = photoURL;
771
+ $[14] = t4;
772
+ } else {
773
+ t4 = $[14];
774
+ }
775
+ const t5 = timeString ? ` · ${timeString}` : "";
776
+ let t6;
777
+ if ($[15] !== displayName || $[16] !== t5) {
778
+ t6 = /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
779
+ displayName,
780
+ t5
781
+ ] });
782
+ $[15] = displayName;
783
+ $[16] = t5;
784
+ $[17] = t6;
785
+ } else {
786
+ t6 = $[17];
787
+ }
788
+ let t7;
789
+ if ($[18] !== t4 || $[19] !== t6) {
790
+ t7 = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-text-secondary dark:text-text-secondary-dark", children: [
791
+ t4,
792
+ t6
793
+ ] });
794
+ $[18] = t4;
795
+ $[19] = t6;
796
+ $[20] = t7;
797
+ } else {
798
+ t7 = $[20];
799
+ }
800
+ return t7;
801
+ }
802
+ function _temp(error) {
803
+ console.error("Error fetching latest history entry:", error);
804
+ }
805
+ function LastEditedByFormAction(t0) {
806
+ const $ = reactCompilerRuntime.c(4);
807
+ const {
808
+ entityId,
809
+ path,
810
+ status,
811
+ collection
812
+ } = t0;
813
+ if (status === "new" || status === "copy" || !entityId) {
814
+ return null;
815
+ }
816
+ if (!collection.history) {
817
+ return null;
818
+ }
819
+ let t1;
820
+ if ($[0] !== collection || $[1] !== entityId || $[2] !== path) {
821
+ t1 = /* @__PURE__ */ jsxRuntime.jsx(LastEditedByIndicator, { path, entityId, collection });
822
+ $[0] = collection;
823
+ $[1] = entityId;
824
+ $[2] = path;
825
+ $[3] = t1;
826
+ } else {
827
+ t1 = $[3];
828
+ }
829
+ return t1;
830
+ }
678
831
  function useEntityHistoryPlugin(props) {
679
832
  const {
680
833
  defaultEnabled = false
@@ -683,6 +836,7 @@
683
836
  if (collection.history === true || defaultEnabled && collection.history !== false) {
684
837
  return {
685
838
  ...collection,
839
+ history: true,
686
840
  entityViews: [...collection.entityViews ?? [], {
687
841
  key: "__history",
688
842
  name: "History",
@@ -703,6 +857,9 @@
703
857
  getUser: props?.getUser
704
858
  }
705
859
  },
860
+ form: {
861
+ BeforeTitle: LastEditedByFormAction
862
+ },
706
863
  collection: {
707
864
  modifyCollection
708
865
  }
@@ -710,6 +867,7 @@
710
867
  }
711
868
  exports2.HistoryControllerContext = HistoryControllerContext;
712
869
  exports2.HistoryControllerProvider = HistoryControllerProvider;
870
+ exports2.LastEditedByIndicator = LastEditedByIndicator;
713
871
  exports2.createHistoryEntry = createHistoryEntry;
714
872
  exports2.useEntityHistoryPlugin = useEntityHistoryPlugin;
715
873
  exports2.useHistoryController = useHistoryController;
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.js","sources":["../src/HistoryControllerProvider.tsx","../src/components/UserChip.tsx","../src/components/EntityHistoryEntry.tsx","../src/components/EntityHistoryView.tsx","../src/entity_history_callbacks.ts","../src/useEntityHistoryPlugin.tsx"],"sourcesContent":["import React, { PropsWithChildren, useContext } from \"react\";\nimport equal from \"react-fast-compare\"\n\nimport { User } from \"@firecms/core\";\n\nexport type HistoryConfigController = {\n /**\n * Function to get a user by uid.\n * @param uid\n */\n getUser?: (uid: string) => User | null;\n}\n\nexport const HistoryControllerContext = React.createContext<HistoryConfigController>({} as any);\nexport const useHistoryController = (): HistoryConfigController => useContext(HistoryControllerContext);\n\n\nexport interface HistoryControllerProviderProps {\n\n getUser?: (uid: string) => User | null;\n\n}\n\nexport const HistoryControllerProvider = React.memo(\n function HistoryControllerProvider({\n children,\n getUser,\n }: PropsWithChildren<HistoryControllerProviderProps>) {\n\n return (\n <HistoryControllerContext.Provider\n value={{\n getUser,\n }}>\n\n {children}\n\n </HistoryControllerContext.Provider>\n );\n }, equal);\n","import { User } from \"@firecms/core\";\nimport { Chip, Tooltip } from \"@firecms/ui\";\n\nexport function UserChip({ user }: { user: User }) {\n return (\n <Tooltip title={user.email ?? user.uid}>\n <Chip size={\"small\"} className={\"flex items-center\"}>\n {user.photoURL && <img\n className={\"rounded-full w-6 h-6 mr-2\"}\n src={user.photoURL} alt={user.displayName ?? \"User picture\"}/>}\n <span>{user.displayName ?? user.email ?? user.uid}</span>\n </Chip>\n </Tooltip>\n );\n}\n","import * as React from \"react\";\n\nimport {\n Chip,\n cls,\n defaultBorderMixin,\n DescriptionIcon,\n IconButton, KeyboardBackspaceIcon,\n KeyboardTabIcon,\n Tooltip,\n Typography\n} from \"@firecms/ui\";\nimport {\n Entity,\n EntityCollection,\n EntityValues,\n getPropertyInPath,\n getValueInPath,\n PreviewSize,\n Property,\n PropertyPreview,\n resolveCollection,\n ResolvedProperty,\n SkeletonPropertyComponent,\n useAuthController,\n useCustomizationController,\n useNavigationController,\n useSideEntityController\n} from \"@firecms/core\";\nimport { useHistoryController } from \"../HistoryControllerProvider\";\nimport { UserChip } from \"./UserChip\";\n\nexport type EntityPreviewProps = {\n size: PreviewSize,\n actions?: React.ReactNode,\n collection?: EntityCollection,\n hover?: boolean;\n previewKeys?: string[],\n entity: Entity<any>,\n previousValues?: EntityValues<any>;\n onClick?: (e: React.SyntheticEvent) => void;\n};\n\nfunction PreviousValueView({\n previousValueInPath,\n childProperty,\n propertyKey\n }: {\n previousValueInPath: any,\n childProperty: Property,\n propertyKey: string\n}) {\n if (typeof previousValueInPath === \"string\" || typeof previousValueInPath === \"number\") {\n return <Typography variant={\"caption\"} color={\"secondary\"} className=\"line-through\">\n {previousValueInPath}\n </Typography>;\n } else if (typeof previousValueInPath === \"boolean\") {\n return <Typography variant={\"caption\"} color={\"secondary\"} className=\"line-through\">\n {previousValueInPath ? \"true\" : \"false\"}\n </Typography>;\n\n } else {\n return <Tooltip\n side={\"left\"}\n title={<div className={\"flex flex-col gap-2\"}>\n <Typography variant={\"caption\"} color={\"secondary\"}>\n Previous value\n </Typography>\n <PropertyPreview\n propertyKey={propertyKey as string}\n value={previousValueInPath}\n property={childProperty as ResolvedProperty}\n size={\"small\"}/>\n </div>}>\n <KeyboardBackspaceIcon size={\"smallest\"} color={\"disabled\"} className={\"mb-1\"}/>\n </Tooltip>\n }\n}\n\n/**\n * This view is used to display a preview of an entity.\n * It is used by default in reference fields and whenever a reference is displayed.\n */\nexport function EntityHistoryEntry({\n actions,\n hover,\n collection: collectionProp,\n previewKeys,\n onClick,\n size,\n entity,\n previousValues\n }: EntityPreviewProps) {\n\n const authController = useAuthController();\n const customizationController = useCustomizationController();\n\n const navigationController = useNavigationController();\n const sideEntityController = useSideEntityController();\n\n const collection = collectionProp ?? navigationController.getCollection(entity.path);\n const updatedOn = entity.values?.[\"__metadata\"]?.[\"updated_on\"];\n if (!collection) {\n throw Error(`Couldn't find the corresponding collection view for the path: ${entity.path}`);\n }\n\n const updatedBy = entity.values?.[\"__metadata\"]?.[\"updated_by\"];\n const { getUser } = useHistoryController();\n const user = getUser?.(updatedBy);\n\n const resolvedCollection = React.useMemo(() => resolveCollection({\n collection,\n path: entity.path,\n values: entity.values,\n propertyConfigs: customizationController.propertyConfigs,\n authController\n }), [collection]);\n\n return <div className={\"w-full flex flex-col gap-2 mt-4\"}>\n <div className={\"ml-4 flex items-center gap-4\"}>\n <Typography variant={\"body2\"} color={\"secondary\"}>{updatedOn.toLocaleString()}</Typography>\n {!user && updatedBy && <Chip size={\"small\"}>{updatedBy}</Chip>}\n {user && <UserChip user={user}/>}\n </div>\n <div\n className={cls(\n \"bg-white dark:bg-surface-900\",\n \"min-h-[44px]\",\n \"w-full\",\n \"items-center\",\n hover ? \"hover:bg-surface-accent-50 dark:hover:bg-surface-800 group-hover:bg-surface-accent-50 dark:group-hover:bg-surface-800\" : \"\",\n size === \"small\" ? \"p-1\" : \"px-2 py-1\",\n \"flex border rounded-lg\",\n onClick ? \"cursor-pointer\" : \"\",\n defaultBorderMixin\n )}>\n\n\n {actions}\n\n {entity &&\n <Tooltip title={\"See details for this revision\"}\n className={\"my-2 grow-0 shrink-0 self-start\"}>\n <IconButton\n color={\"inherit\"}\n className={\"\"}\n onClick={(e) => {\n\n sideEntityController.open({\n entityId: entity.id,\n path: entity.path,\n allowFullScreen: false,\n collection: {\n ...collection,\n subcollections: undefined,\n entityViews: undefined,\n permissions: {\n create: false,\n delete: false,\n edit: false,\n read: true\n }\n },\n updateUrl: true\n });\n }}>\n <KeyboardTabIcon/>\n </IconButton>\n </Tooltip>}\n\n <div className={\"flex flex-col grow w-full m-1 shrink min-w-0\"}>\n\n {previewKeys && previewKeys.map((key) => {\n const childProperty = getPropertyInPath(resolvedCollection.properties, key);\n\n const valueInPath = getValueInPath(entity.values, key);\n const previousValueInPath = previousValues ? getValueInPath(previousValues, key) : undefined;\n\n const element = childProperty ? (entity\n ? <PropertyPreview\n propertyKey={key as string}\n value={valueInPath}\n property={childProperty as ResolvedProperty}\n size={\"small\"}/>\n : <SkeletonPropertyComponent\n property={childProperty as ResolvedProperty}\n size={\"small\"}/>) :\n <Typography variant={\"body2\"}>\n {typeof valueInPath === \"string\" ? valueInPath : JSON.stringify(valueInPath)}\n </Typography>;\n return (\n <div key={\"ref_prev_\" + key}\n className=\"flex w-full my-1 items-center\">\n <Typography variant={\"caption\"}\n color={\"secondary\"}\n className=\"min-w-[140px] md:min-w-[200px] w-1/5 pr-8 overflow-hidden text-ellipsis text-right\">\n {key}\n </Typography>\n <div className=\"w-4/5\">\n {previousValueInPath !== undefined && previousValueInPath !== valueInPath &&\n <PreviousValueView previousValueInPath={previousValueInPath}\n childProperty={childProperty as ResolvedProperty}\n propertyKey={key}/>\n }\n {element}\n </div>\n </div>\n );\n })}\n\n </div>\n\n </div>\n </div>\n}\n\n","import { useEffect, useRef, useState } from \"react\";\nimport {\n ConfirmationDialog,\n Entity,\n EntityCustomViewParams,\n EntityView,\n ErrorBoundary,\n useAuthController,\n useDataSource,\n useSnackbarController\n} from \"@firecms/core\";\nimport { cls, HistoryIcon, IconButton, Label, Tooltip, Typography } from \"@firecms/ui\";\nimport { EntityHistoryEntry } from \"./EntityHistoryEntry\";\n\nexport function EntityHistoryView({\n entity,\n collection,\n formContext\n }: EntityCustomViewParams) {\n\n const authController = useAuthController();\n const snackbarController = useSnackbarController();\n const dirty = formContext?.formex.dirty;\n\n const dataSource = useDataSource();\n const pathAndId = entity ? entity?.path + \"/\" + entity?.id : undefined;\n\n const [revertVersionDialog, setRevertVersionDialog] = useState<Entity | undefined>(undefined);\n const [revisions, setRevisions] = useState<Entity[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [hasMore, setHasMore] = useState(true);\n\n const PAGE_SIZE = 5;\n const [limit, setLimit] = useState(PAGE_SIZE);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const observerRef = useRef<IntersectionObserver | null>(null);\n const loadMoreRef = useRef<HTMLDivElement>(null);\n\n // Load revisions with the current limit\n useEffect(() => {\n if (!pathAndId) return;\n\n setIsLoading(true); // Set loading true when fetching starts\n const listener = dataSource.listenCollection?.({\n path: pathAndId + \"/__history\",\n collection: collection,\n order: \"desc\",\n orderBy: \"__metadata.updated_on\",\n limit: limit,\n startAfter: undefined,\n onUpdate: (entities) => {\n setRevisions(entities);\n setHasMore(entities.length === limit && entities.length >= PAGE_SIZE); // Ensure we fetched a full page to consider hasMore\n setIsLoading(false);\n },\n onError: (error) => {\n console.error(\"Error fetching history:\", error);\n setIsLoading(false);\n setHasMore(false); // Stop trying if there's an error\n }\n });\n return () => {\n if (typeof listener === \"function\") {\n listener();\n }\n };\n }, [pathAndId, limit, dataSource]);\n\n // Setup intersection observer for infinite scroll\n useEffect(() => {\n const currentContainer = containerRef.current;\n const currentLoadMore = loadMoreRef.current;\n\n // Conditions for active observation\n if (!currentContainer || !currentLoadMore || !hasMore || isLoading) {\n // If we shouldn't be observing, ensure any existing observer is disconnected\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n return;\n }\n\n // Options for the IntersectionObserver\n const options = {\n root: currentContainer,\n rootMargin: \"0px 0px 200px 0px\", // Trigger 200px before the sentinel is at the bottom edge\n threshold: 0.01 // Trigger if even a small part is visible within the rootMargin\n };\n\n // The callback for when the sentinel's intersection state changes\n const handleObserver = (entries: IntersectionObserverEntry[]) => {\n const target = entries[0];\n if (target.isIntersecting && hasMore && !isLoading) {\n // No need to setIsLoading(true) here, it's done in the data fetching useEffect\n setLimit(prev => prev + PAGE_SIZE);\n }\n };\n\n const observer = new IntersectionObserver(handleObserver, options);\n observer.observe(currentLoadMore);\n observerRef.current = observer; // Store the new observer\n\n // Cleanup function for this effect instance\n return () => {\n observer.disconnect(); // Disconnect the observer created in *this* effect run\n if (observerRef.current === observer) {\n observerRef.current = null;\n }\n };\n // Re-run if hasMore, isLoading changes, or if revisions.length changes (which might make loadMoreRef available/unavailable)\n }, [hasMore, isLoading, revisions.length]);\n\n if (!entity) {\n return <div className=\"flex items-center justify-center h-full\">\n <Label>History is only available for existing entities</Label>\n </div>\n }\n\n function doRevert(revertVersion: Entity) {\n if (!entity) {\n throw new Error(\"No entity to revert\");\n }\n const revertValues = {\n ...revertVersion.values,\n __metadata: {\n ...revertVersion.values?.[\"__metadata\"],\n reverted: true,\n updated_on: new Date(),\n updated_by: authController.user?.uid ?? null,\n }\n };\n const saveReverted = dataSource.saveEntity({\n path: entity.path,\n entityId: entity.id,\n values: revertValues,\n collection,\n status: \"existing\"\n });\n const saveRevertedHistory = dataSource.saveEntity({\n path: revertVersion.path,\n entityId: revertVersion.id,\n values: revertValues,\n collection,\n status: \"existing\"\n });\n return Promise.all([saveReverted, saveRevertedHistory])\n .then(() => {\n formContext.formex.resetForm({\n values: revertVersion.values\n });\n setRevertVersionDialog(undefined);\n snackbarController.open({\n message: \"Reverted version\",\n type: \"info\"\n });\n }\n ).catch((error) => {\n console.error(\"Error reverting entity:\", error);\n snackbarController.open({\n message: \"Error reverting entity\",\n type: \"error\"\n });\n });\n\n }\n\n return <div\n ref={containerRef}\n className={cls(\"relative flex-1 h-full overflow-auto w-full flex flex-col gap-4 p-8\")}>\n <div className=\"flex flex-col gap-2 max-w-6xl mx-auto w-full\">\n\n <Typography variant={\"h5\"} className={\"mt-24 ml-4\"}>\n History\n </Typography>\n\n {revisions.length === 0 && <>\n <Label className={\"ml-4 mt-8\"}>\n No history available\n </Label>\n <Typography variant={\"caption\"} className={\"ml-4\"}>\n When you save an entity, a new version is created and stored in the history.\n </Typography>\n </>}\n\n {revisions.map((revision, index) => {\n const previewKeys = revision.values?.[\"__metadata\"]?.[\"changed_fields\"];\n const previousValues: object | undefined = revision.values?.[\"__metadata\"]?.[\"previous_values\"];\n return <div key={index} className=\"flex flex-cols gap-2 w-full\">\n <EntityHistoryEntry size={\"large\"}\n entity={revision}\n collection={collection}\n previewKeys={previewKeys}\n previousValues={previousValues}\n actions={\n <Tooltip title={\"Revert to this version\"}\n className={\"m-2 grow-0 self-start\"}>\n <IconButton\n onClick={() => {\n if (dirty) {\n snackbarController.open({\n message: \"Please save or discard your changes before reverting\",\n type: \"warning\"\n });\n } else {\n setRevertVersionDialog(revision);\n }\n }}>\n <HistoryIcon/>\n </IconButton>\n </Tooltip>}\n />\n </div>\n })}\n\n {/* Load more sentinel element */}\n {revisions.length > 0 && (\n <div\n ref={loadMoreRef}\n className=\"py-4 text-center\"\n >\n {isLoading && <Label>Loading more...</Label>}\n {!hasMore && revisions.length > PAGE_SIZE && <Label>No more history available</Label>}\n </div>\n )}\n </div>\n\n <ErrorBoundary>\n <ConfirmationDialog open={Boolean(revertVersionDialog)}\n onAccept={function (): void {\n if (!revertVersionDialog) return;\n doRevert(revertVersionDialog);\n }}\n onCancel={function (): void {\n setRevertVersionDialog(undefined);\n }}\n title={<Typography variant={\"subtitle2\"}>Revert data to this version?</Typography>}\n body={revertVersionDialog ?\n <EntityView entity={revertVersionDialog}\n collection={collection}\n path={entity?.path}/> : null}/>\n </ErrorBoundary>\n </div>\n}\n","import { EntityCallbacks, FireCMSContext, User } from \"@firecms/core\";\nimport equal from \"react-fast-compare\"\nimport { HistoryEntry, NewHistoryEntryParams } from \"./types\";\n\n\n\nexport function createHistoryEntry<T = any>({\n context,\n previousValues,\n values,\n path,\n entityId,\n collection\n}: NewHistoryEntryParams<T>) {\n\n const uid = context.authController.user?.uid;\n const dataSource = context.dataSource;\n const changedFields = previousValues ? findChangedFields(previousValues as object, values as object) : null;\n\n const entry: HistoryEntry<T> = {\n ...values,\n __metadata: {\n previous_values: previousValues,\n changed_fields: changedFields,\n updated_on: new Date(),\n updated_by: uid ?? null,\n }\n };\n dataSource.saveEntity({\n path: path + \"/\" + entityId + \"/__history\",\n values: entry,\n status: \"new\",\n collection\n }).then(() => {\n console.debug(\"History saved for\", path, entityId);\n });\n}\n\nexport const entityHistoryCallbacks: EntityCallbacks = {\n onSaveSuccess: async (props) => {\n\n const values = props.values;\n const previousValues = props.previousValues;\n const path = props.path;\n const entityId = props.entityId;\n const context = props.context;\n const collection = props.collection;\n createHistoryEntry({\n context: context,\n previousValues: previousValues,\n values: values,\n path: path,\n entityId: entityId,\n collection: collection\n });\n }\n}\n\nfunction findChangedFields<M extends object>(oldValues: M, newValues: M, prefix: string = \"\"): string[] {\n const changedFields: string[] = [];\n\n // Handle null/undefined cases\n if (equal(oldValues, newValues)) return changedFields;\n if (!oldValues || !newValues) return [prefix || \".\"];\n\n // Get all unique keys from both objects\n const allKeys = new Set([\n ...Object.keys(oldValues),\n ...Object.keys(newValues)\n ]);\n\n for (const key of allKeys) {\n const oldValue = oldValues[key as keyof M];\n const newValue = newValues[key as keyof M];\n const currentPath = prefix ? `${prefix}.${key}` : key;\n\n // If key exists only in one object\n if ((key in oldValues) !== (key in newValues)) {\n changedFields.push(currentPath);\n continue;\n }\n\n // If values are identical (deep equality)\n if (equal(oldValue, newValue)) continue;\n\n // Handle arrays\n if (Array.isArray(oldValue) && Array.isArray(newValue)) {\n if (oldValue.length !== newValue.length) {\n changedFields.push(currentPath);\n } else {\n // Check if any array element changed\n for (let i = 0; i < oldValue.length; i++) {\n if (\n typeof oldValue[i] === \"object\" && oldValue[i] !== null &&\n typeof newValue[i] === \"object\" && newValue[i] !== null\n ) {\n const nestedChanges = findChangedFields(\n oldValue[i] as object,\n newValue[i] as object,\n `${currentPath}[${i}]`\n );\n if (nestedChanges.length > 0) {\n changedFields.push(currentPath);\n break;\n }\n } else if (!equal(oldValue[i], newValue[i])) {\n changedFields.push(currentPath);\n break;\n }\n }\n }\n }\n // Handle nested objects\n else if (\n typeof oldValue === \"object\" && oldValue !== null &&\n typeof newValue === \"object\" && newValue !== null\n ) {\n const nestedChanges = findChangedFields(\n oldValue as object,\n newValue as object,\n currentPath\n );\n changedFields.push(...nestedChanges);\n }\n // Handle primitives\n else {\n changedFields.push(currentPath);\n }\n }\n\n return changedFields;\n}\n","import { useCallback, useMemo } from \"react\";\nimport { EntityCollection, FireCMSPlugin, mergeCallbacks, User } from \"@firecms/core\";\nimport { EntityHistoryView } from \"./components/EntityHistoryView\";\nimport { HistoryIcon } from \"@firecms/ui\";\nimport { entityHistoryCallbacks } from \"./entity_history_callbacks\";\nimport { HistoryControllerProvider } from \"./HistoryControllerProvider\";\n\n/**\n * This plugin adds a history view to the entity side panel.\n */\nexport function useEntityHistoryPlugin(props?: EntityHistoryPluginProps): FireCMSPlugin<any, any, any, EntityHistoryPluginProps> {\n\n const { defaultEnabled = false } = props ?? {};\n\n const modifyCollection = useCallback((collection: EntityCollection) => {\n if (collection.history === true || (defaultEnabled && collection.history !== false)) {\n return {\n ...collection,\n entityViews: [\n ...(collection.entityViews ?? []),\n {\n key: \"__history\",\n name: \"History\",\n tabComponent: <HistoryIcon size={\"small\"}/>,\n Builder: EntityHistoryView,\n position: \"start\"\n }\n ],\n callbacks: mergeCallbacks(collection.callbacks, entityHistoryCallbacks)\n } satisfies EntityCollection;\n }\n return collection;\n }, []);\n\n return useMemo(() => ({\n key: \"entity_history\",\n provider: {\n Component: HistoryControllerProvider,\n props: {\n getUser: props?.getUser\n }\n },\n collection: {\n modifyCollection\n }\n } satisfies FireCMSPlugin), [props]);\n}\n\nexport type EntityHistoryPluginProps = {\n /**\n * If true, the history view will be enabled to all collections by default.\n * Each collection can override this value by setting the `history` property.\n */\n defaultEnabled?: boolean;\n\n /**\n * Function to get the user object from the uid.\n * @param uid\n */\n getUser?: (uid: string) => User | null;\n}\n"],"names":["HistoryControllerContext","React","createContext","useHistoryController","useContext","HistoryControllerProvider","memo","t0","$","_c","children","getUser","t1","t2","equal","UserChip","user","email","uid","displayName","photoURL","jsx","t3","t4","t5","Chip","t6","Tooltip","PreviousValueView","previousValueInPath","childProperty","propertyKey","Typography","Symbol","for","jsxs","PropertyPreview","KeyboardBackspaceIcon","EntityHistoryEntry","actions","hover","collection","collectionProp","previewKeys","onClick","size","entity","previousValues","authController","useAuthController","customizationController","useCustomizationController","navigationController","useNavigationController","sideEntityController","useSideEntityController","getCollection","path","updatedOn","values","Error","updatedBy","resolvedCollection","useMemo","resolveCollection","propertyConfigs","toLocaleString","cls","defaultBorderMixin","IconButton","e","open","entityId","id","allowFullScreen","subcollections","undefined","entityViews","permissions","create","delete","edit","read","updateUrl","KeyboardTabIcon","map","key","getPropertyInPath","properties","valueInPath","getValueInPath","element","SkeletonPropertyComponent","JSON","stringify","EntityHistoryView","formContext","snackbarController","useSnackbarController","dirty","formex","dataSource","useDataSource","pathAndId","revertVersionDialog","setRevertVersionDialog","useState","revisions","setRevisions","isLoading","setIsLoading","hasMore","setHasMore","limit","setLimit","containerRef","useRef","observerRef","loadMoreRef","listener","listenCollection","order","orderBy","startAfter","onUpdate","entities","length","onError","error","console","useEffect","currentContainer","current","currentLoadMore","disconnect","options","root","rootMargin","threshold","handleObserver","entries","target","isIntersecting","_temp","observer","IntersectionObserver","observe","Label","doRevert","revertVersion","revertValues","__metadata","reverted","updated_on","Date","updated_by","saveReverted","saveEntity","status","saveRevertedHistory","Promise","all","then","resetForm","message","type","catch","error_0","t7","t8","t9","Fragment","t10","t11","revision","index","changed_fields","previous_values","HistoryIcon","t12","t13","Boolean","t14","t15","t16","t17","EntityView","t18","ErrorBoundary","ConfirmationDialog","t19","prev","createHistoryEntry","context","changedFields","findChangedFields","entry","debug","entityHistoryCallbacks","onSaveSuccess","props","oldValues","newValues","prefix","allKeys","Set","Object","keys","oldValue","newValue","currentPath","push","Array","isArray","i","nestedChanges","useEntityHistoryPlugin","defaultEnabled","modifyCollection","useCallback","history","name","tabComponent","Builder","position","callbacks","mergeCallbacks","provider","Component"],"mappings":"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"}
1
+ {"version":3,"file":"index.umd.js","sources":["../src/HistoryControllerProvider.tsx","../src/components/UserChip.tsx","../src/components/EntityHistoryEntry.tsx","../src/components/EntityHistoryView.tsx","../src/entity_history_callbacks.ts","../src/components/LastEditedByIndicator.tsx","../src/components/LastEditedByPluginComponents.tsx","../src/useEntityHistoryPlugin.tsx"],"sourcesContent":["import React, { PropsWithChildren, useContext } from \"react\";\nimport equal from \"react-fast-compare\"\n\nimport { User } from \"@firecms/core\";\n\nexport type HistoryConfigController = {\n /**\n * Function to get a user by uid.\n * @param uid\n */\n getUser?: (uid: string) => User | null;\n}\n\nexport const HistoryControllerContext = React.createContext<HistoryConfigController>({} as any);\nexport const useHistoryController = (): HistoryConfigController => useContext(HistoryControllerContext);\n\n\nexport interface HistoryControllerProviderProps {\n\n getUser?: (uid: string) => User | null;\n\n}\n\nexport const HistoryControllerProvider = React.memo(\n function HistoryControllerProvider({\n children,\n getUser,\n }: PropsWithChildren<HistoryControllerProviderProps>) {\n\n return (\n <HistoryControllerContext.Provider\n value={{\n getUser,\n }}>\n\n {children}\n\n </HistoryControllerContext.Provider>\n );\n }, equal);\n","import { User } from \"@firecms/core\";\nimport { Chip, Tooltip } from \"@firecms/ui\";\n\nexport function UserChip({ user }: { user: User }) {\n return (\n <Tooltip title={user.email ?? user.uid}>\n <Chip size={\"small\"} className={\"flex items-center\"}>\n {user.photoURL && <img\n className={\"rounded-full w-6 h-6 mr-2\"}\n src={user.photoURL} alt={user.displayName ?? \"User picture\"}/>}\n <span>{user.displayName ?? user.email ?? user.uid}</span>\n </Chip>\n </Tooltip>\n );\n}\n","import * as React from \"react\";\n\nimport {\n Chip,\n cls,\n defaultBorderMixin,\n DescriptionIcon,\n IconButton, KeyboardBackspaceIcon,\n KeyboardTabIcon,\n Tooltip,\n Typography\n} from \"@firecms/ui\";\nimport {\n Entity,\n EntityCollection,\n EntityValues,\n getPropertyInPath,\n getValueInPath,\n PreviewSize,\n Property,\n PropertyPreview,\n resolveCollection,\n ResolvedProperty,\n SkeletonPropertyComponent,\n useAuthController,\n useCustomizationController,\n useNavigationController,\n useSideEntityController\n} from \"@firecms/core\";\nimport { useHistoryController } from \"../HistoryControllerProvider\";\nimport { UserChip } from \"./UserChip\";\n\nexport type EntityPreviewProps = {\n size: PreviewSize,\n actions?: React.ReactNode,\n collection?: EntityCollection,\n hover?: boolean;\n previewKeys?: string[],\n entity: Entity<any>,\n previousValues?: EntityValues<any>;\n onClick?: (e: React.SyntheticEvent) => void;\n};\n\nfunction PreviousValueView({\n previousValueInPath,\n childProperty,\n propertyKey\n }: {\n previousValueInPath: any,\n childProperty: Property,\n propertyKey: string\n}) {\n if (typeof previousValueInPath === \"string\" || typeof previousValueInPath === \"number\") {\n return <Typography variant={\"caption\"} color={\"secondary\"} className=\"line-through\">\n {previousValueInPath}\n </Typography>;\n } else if (typeof previousValueInPath === \"boolean\") {\n return <Typography variant={\"caption\"} color={\"secondary\"} className=\"line-through\">\n {previousValueInPath ? \"true\" : \"false\"}\n </Typography>;\n\n } else {\n return <Tooltip\n side={\"left\"}\n title={<div className={\"flex flex-col gap-2\"}>\n <Typography variant={\"caption\"} color={\"secondary\"}>\n Previous value\n </Typography>\n <PropertyPreview\n propertyKey={propertyKey as string}\n value={previousValueInPath}\n property={childProperty as ResolvedProperty}\n size={\"small\"}/>\n </div>}>\n <KeyboardBackspaceIcon size={\"smallest\"} color={\"disabled\"} className={\"mb-1\"}/>\n </Tooltip>\n }\n}\n\n/**\n * This view is used to display a preview of an entity.\n * It is used by default in reference fields and whenever a reference is displayed.\n */\nexport function EntityHistoryEntry({\n actions,\n hover,\n collection: collectionProp,\n previewKeys,\n onClick,\n size,\n entity,\n previousValues\n }: EntityPreviewProps) {\n\n const authController = useAuthController();\n const customizationController = useCustomizationController();\n\n const navigationController = useNavigationController();\n const sideEntityController = useSideEntityController();\n\n const collection = collectionProp ?? navigationController.getCollection(entity.path);\n const updatedOn = entity.values?.[\"__metadata\"]?.[\"updated_on\"];\n if (!collection) {\n throw Error(`Couldn't find the corresponding collection view for the path: ${entity.path}`);\n }\n\n const updatedBy = entity.values?.[\"__metadata\"]?.[\"updated_by\"];\n const { getUser } = useHistoryController();\n const user = getUser?.(updatedBy);\n\n const resolvedCollection = React.useMemo(() => resolveCollection({\n collection,\n path: entity.path,\n values: entity.values,\n propertyConfigs: customizationController.propertyConfigs,\n authController\n }), [collection]);\n\n return <div className={\"w-full flex flex-col gap-2 mt-4\"}>\n <div className={\"ml-4 flex items-center gap-4\"}>\n <Typography variant={\"body2\"} color={\"secondary\"}>{updatedOn.toLocaleString()}</Typography>\n {!user && updatedBy && <Chip size={\"small\"}>{updatedBy}</Chip>}\n {user && <UserChip user={user}/>}\n </div>\n <div\n className={cls(\n \"bg-white dark:bg-surface-900\",\n \"min-h-[44px]\",\n \"w-full\",\n \"items-center\",\n hover ? \"hover:bg-surface-accent-50 dark:hover:bg-surface-800 group-hover:bg-surface-accent-50 dark:group-hover:bg-surface-800\" : \"\",\n size === \"small\" ? \"p-1\" : \"px-2 py-1\",\n \"flex border rounded-lg\",\n onClick ? \"cursor-pointer\" : \"\",\n defaultBorderMixin\n )}>\n\n\n {actions}\n\n {entity &&\n <Tooltip title={\"See details for this revision\"}\n className={\"my-2 grow-0 shrink-0 self-start\"}>\n <IconButton\n color={\"inherit\"}\n className={\"\"}\n onClick={(e) => {\n\n sideEntityController.open({\n entityId: entity.id,\n path: entity.path,\n allowFullScreen: false,\n collection: {\n ...collection,\n subcollections: undefined,\n entityViews: undefined,\n permissions: {\n create: false,\n delete: false,\n edit: false,\n read: true\n }\n },\n updateUrl: true\n });\n }}>\n <KeyboardTabIcon/>\n </IconButton>\n </Tooltip>}\n\n <div className={\"flex flex-col grow w-full m-1 shrink min-w-0\"}>\n\n {previewKeys && previewKeys.map((key) => {\n const childProperty = getPropertyInPath(resolvedCollection.properties, key);\n\n const valueInPath = getValueInPath(entity.values, key);\n const previousValueInPath = previousValues ? getValueInPath(previousValues, key) : undefined;\n\n const element = childProperty ? (entity\n ? <PropertyPreview\n propertyKey={key as string}\n value={valueInPath}\n property={childProperty as ResolvedProperty}\n size={\"small\"}/>\n : <SkeletonPropertyComponent\n property={childProperty as ResolvedProperty}\n size={\"small\"}/>) :\n <Typography variant={\"body2\"}>\n {typeof valueInPath === \"string\" ? valueInPath : JSON.stringify(valueInPath)}\n </Typography>;\n return (\n <div key={\"ref_prev_\" + key}\n className=\"flex w-full my-1 items-center\">\n <Typography variant={\"caption\"}\n color={\"secondary\"}\n className=\"min-w-[140px] md:min-w-[200px] w-1/5 pr-8 overflow-hidden text-ellipsis text-right\">\n {key}\n </Typography>\n <div className=\"w-4/5\">\n {previousValueInPath !== undefined && previousValueInPath !== valueInPath &&\n <PreviousValueView previousValueInPath={previousValueInPath}\n childProperty={childProperty as ResolvedProperty}\n propertyKey={key}/>\n }\n {element}\n </div>\n </div>\n );\n })}\n\n </div>\n\n </div>\n </div>\n}\n\n","import { useEffect, useRef, useState } from \"react\";\nimport {\n ConfirmationDialog,\n Entity,\n EntityCustomViewParams,\n EntityView,\n ErrorBoundary,\n useAuthController,\n useDataSource,\n useSnackbarController\n} from \"@firecms/core\";\nimport { cls, HistoryIcon, IconButton, Label, Tooltip, Typography } from \"@firecms/ui\";\nimport { EntityHistoryEntry } from \"./EntityHistoryEntry\";\n\nexport function EntityHistoryView({\n entity,\n collection,\n formContext\n }: EntityCustomViewParams) {\n\n const authController = useAuthController();\n const snackbarController = useSnackbarController();\n const dirty = formContext?.formex.dirty;\n\n const dataSource = useDataSource();\n const pathAndId = entity ? entity?.path + \"/\" + entity?.id : undefined;\n\n const [revertVersionDialog, setRevertVersionDialog] = useState<Entity | undefined>(undefined);\n const [revisions, setRevisions] = useState<Entity[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [hasMore, setHasMore] = useState(true);\n\n const PAGE_SIZE = 5;\n const [limit, setLimit] = useState(PAGE_SIZE);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const observerRef = useRef<IntersectionObserver | null>(null);\n const loadMoreRef = useRef<HTMLDivElement>(null);\n\n // Load revisions with the current limit\n useEffect(() => {\n if (!pathAndId) return;\n\n setIsLoading(true); // Set loading true when fetching starts\n const listener = dataSource.listenCollection?.({\n path: pathAndId + \"/__history\",\n collection: collection,\n order: \"desc\",\n orderBy: \"__metadata.updated_on\",\n limit: limit,\n startAfter: undefined,\n onUpdate: (entities) => {\n setRevisions(entities);\n setHasMore(entities.length === limit && entities.length >= PAGE_SIZE); // Ensure we fetched a full page to consider hasMore\n setIsLoading(false);\n },\n onError: (error) => {\n console.error(\"Error fetching history:\", error);\n setIsLoading(false);\n setHasMore(false); // Stop trying if there's an error\n }\n });\n return () => {\n if (typeof listener === \"function\") {\n listener();\n }\n };\n }, [pathAndId, limit, dataSource]);\n\n // Setup intersection observer for infinite scroll\n useEffect(() => {\n const currentContainer = containerRef.current;\n const currentLoadMore = loadMoreRef.current;\n\n // Conditions for active observation\n if (!currentContainer || !currentLoadMore || !hasMore || isLoading) {\n // If we shouldn't be observing, ensure any existing observer is disconnected\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n return;\n }\n\n // Options for the IntersectionObserver\n const options = {\n root: currentContainer,\n rootMargin: \"0px 0px 200px 0px\", // Trigger 200px before the sentinel is at the bottom edge\n threshold: 0.01 // Trigger if even a small part is visible within the rootMargin\n };\n\n // The callback for when the sentinel's intersection state changes\n const handleObserver = (entries: IntersectionObserverEntry[]) => {\n const target = entries[0];\n if (target.isIntersecting && hasMore && !isLoading) {\n // No need to setIsLoading(true) here, it's done in the data fetching useEffect\n setLimit(prev => prev + PAGE_SIZE);\n }\n };\n\n const observer = new IntersectionObserver(handleObserver, options);\n observer.observe(currentLoadMore);\n observerRef.current = observer; // Store the new observer\n\n // Cleanup function for this effect instance\n return () => {\n observer.disconnect(); // Disconnect the observer created in *this* effect run\n if (observerRef.current === observer) {\n observerRef.current = null;\n }\n };\n // Re-run if hasMore, isLoading changes, or if revisions.length changes (which might make loadMoreRef available/unavailable)\n }, [hasMore, isLoading, revisions.length]);\n\n if (!entity) {\n return <div className=\"flex items-center justify-center h-full\">\n <Label>History is only available for existing entities</Label>\n </div>\n }\n\n function doRevert(revertVersion: Entity) {\n if (!entity) {\n throw new Error(\"No entity to revert\");\n }\n const revertValues = {\n ...revertVersion.values,\n __metadata: {\n ...revertVersion.values?.[\"__metadata\"],\n reverted: true,\n updated_on: new Date(),\n updated_by: authController.user?.uid ?? null,\n }\n };\n const saveReverted = dataSource.saveEntity({\n path: entity.path,\n entityId: entity.id,\n values: revertValues,\n collection,\n status: \"existing\"\n });\n const saveRevertedHistory = dataSource.saveEntity({\n path: revertVersion.path,\n entityId: revertVersion.id,\n values: revertValues,\n collection,\n status: \"existing\"\n });\n return Promise.all([saveReverted, saveRevertedHistory])\n .then(() => {\n formContext.formex.resetForm({\n values: revertVersion.values\n });\n setRevertVersionDialog(undefined);\n snackbarController.open({\n message: \"Reverted version\",\n type: \"info\"\n });\n }\n ).catch((error) => {\n console.error(\"Error reverting entity:\", error);\n snackbarController.open({\n message: \"Error reverting entity\",\n type: \"error\"\n });\n });\n\n }\n\n return <div\n ref={containerRef}\n className={cls(\"relative flex-1 h-full overflow-auto w-full flex flex-col gap-4 p-8\")}>\n <div className=\"flex flex-col gap-2 max-w-6xl mx-auto w-full\">\n\n <Typography variant={\"h5\"} className={\"mt-24 ml-4\"}>\n History\n </Typography>\n\n {revisions.length === 0 && <>\n <Label className={\"ml-4 mt-8\"}>\n No history available\n </Label>\n <Typography variant={\"caption\"} className={\"ml-4\"}>\n When you save an entity, a new version is created and stored in the history.\n </Typography>\n </>}\n\n {revisions.map((revision, index) => {\n const previewKeys = revision.values?.[\"__metadata\"]?.[\"changed_fields\"];\n const previousValues: object | undefined = revision.values?.[\"__metadata\"]?.[\"previous_values\"];\n return <div key={index} className=\"flex flex-cols gap-2 w-full\">\n <EntityHistoryEntry size={\"large\"}\n entity={revision}\n collection={collection}\n previewKeys={previewKeys}\n previousValues={previousValues}\n actions={\n <Tooltip title={\"Revert to this version\"}\n className={\"m-2 grow-0 self-start\"}>\n <IconButton\n onClick={() => {\n if (dirty) {\n snackbarController.open({\n message: \"Please save or discard your changes before reverting\",\n type: \"warning\"\n });\n } else {\n setRevertVersionDialog(revision);\n }\n }}>\n <HistoryIcon/>\n </IconButton>\n </Tooltip>}\n />\n </div>\n })}\n\n {/* Load more sentinel element */}\n {revisions.length > 0 && (\n <div\n ref={loadMoreRef}\n className=\"py-4 text-center\"\n >\n {isLoading && <Label>Loading more...</Label>}\n {!hasMore && revisions.length > PAGE_SIZE && <Label>No more history available</Label>}\n </div>\n )}\n </div>\n\n <ErrorBoundary>\n <ConfirmationDialog open={Boolean(revertVersionDialog)}\n onAccept={function (): void {\n if (!revertVersionDialog) return;\n doRevert(revertVersionDialog);\n }}\n onCancel={function (): void {\n setRevertVersionDialog(undefined);\n }}\n title={<Typography variant={\"subtitle2\"}>Revert data to this version?</Typography>}\n body={revertVersionDialog ?\n <EntityView entity={revertVersionDialog}\n collection={collection}\n path={entity?.path}/> : null}/>\n </ErrorBoundary>\n </div>\n}\n","import { EntityCallbacks, FireCMSContext, User } from \"@firecms/core\";\nimport equal from \"react-fast-compare\"\nimport { HistoryEntry, NewHistoryEntryParams } from \"./types\";\n\n\n\nexport function createHistoryEntry<T = any>({\n context,\n previousValues,\n values,\n path,\n entityId,\n collection\n}: NewHistoryEntryParams<T>) {\n\n const uid = context.authController.user?.uid;\n const dataSource = context.dataSource;\n const changedFields = previousValues ? findChangedFields(previousValues as object, values as object) : null;\n\n const entry: HistoryEntry<T> = {\n ...values,\n __metadata: {\n previous_values: previousValues,\n changed_fields: changedFields,\n updated_on: new Date(),\n updated_by: uid ?? null,\n }\n };\n dataSource.saveEntity({\n path: path + \"/\" + entityId + \"/__history\",\n values: entry,\n status: \"new\",\n collection\n }).then(() => {\n console.debug(\"History saved for\", path, entityId);\n });\n}\n\nexport const entityHistoryCallbacks: EntityCallbacks = {\n onSaveSuccess: async (props) => {\n\n const values = props.values;\n const previousValues = props.previousValues;\n const path = props.path;\n const entityId = props.entityId;\n const context = props.context;\n const collection = props.collection;\n createHistoryEntry({\n context: context,\n previousValues: previousValues,\n values: values,\n path: path,\n entityId: entityId,\n collection: collection\n });\n }\n}\n\nfunction findChangedFields<M extends object>(oldValues: M, newValues: M, prefix: string = \"\"): string[] {\n const changedFields: string[] = [];\n\n // Handle null/undefined cases\n if (equal(oldValues, newValues)) return changedFields;\n if (!oldValues || !newValues) return [prefix || \".\"];\n\n // Get all unique keys from both objects\n const allKeys = new Set([\n ...Object.keys(oldValues),\n ...Object.keys(newValues)\n ]);\n\n for (const key of allKeys) {\n const oldValue = oldValues[key as keyof M];\n const newValue = newValues[key as keyof M];\n const currentPath = prefix ? `${prefix}.${key}` : key;\n\n // If key exists only in one object\n if ((key in oldValues) !== (key in newValues)) {\n changedFields.push(currentPath);\n continue;\n }\n\n // If values are identical (deep equality)\n if (equal(oldValue, newValue)) continue;\n\n // Handle arrays\n if (Array.isArray(oldValue) && Array.isArray(newValue)) {\n if (oldValue.length !== newValue.length) {\n changedFields.push(currentPath);\n } else {\n // Check if any array element changed\n for (let i = 0; i < oldValue.length; i++) {\n if (\n typeof oldValue[i] === \"object\" && oldValue[i] !== null &&\n typeof newValue[i] === \"object\" && newValue[i] !== null\n ) {\n const nestedChanges = findChangedFields(\n oldValue[i] as object,\n newValue[i] as object,\n `${currentPath}[${i}]`\n );\n if (nestedChanges.length > 0) {\n changedFields.push(currentPath);\n break;\n }\n } else if (!equal(oldValue[i], newValue[i])) {\n changedFields.push(currentPath);\n break;\n }\n }\n }\n }\n // Handle nested objects\n else if (\n typeof oldValue === \"object\" && oldValue !== null &&\n typeof newValue === \"object\" && newValue !== null\n ) {\n const nestedChanges = findChangedFields(\n oldValue as object,\n newValue as object,\n currentPath\n );\n changedFields.push(...nestedChanges);\n }\n // Handle primitives\n else {\n changedFields.push(currentPath);\n }\n }\n\n return changedFields;\n}\n","import React, { useEffect, useState } from \"react\";\nimport { Entity, useDataSource, User } from \"@firecms/core\";\nimport { useHistoryController } from \"../HistoryControllerProvider\";\n\nfunction getRelativeTimeString(date: Date): string {\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffSeconds = Math.floor(diffMs / 1000);\n const diffMinutes = Math.floor(diffSeconds / 60);\n const diffHours = Math.floor(diffMinutes / 60);\n const diffDays = Math.floor(diffHours / 24);\n\n if (diffSeconds < 60) return \"just now\";\n if (diffMinutes < 60) return `${diffMinutes}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n if (diffDays < 30) return `${diffDays}d ago`;\n return date.toLocaleDateString();\n}\n\n/**\n * Fetches the latest history entry from the __history subcollection\n * and displays who last edited the entity and when.\n */\nexport function LastEditedByIndicator({\n path,\n entityId,\n collection\n}: {\n path: string;\n entityId: string;\n collection: any;\n}) {\n const { getUser } = useHistoryController();\n const dataSource = useDataSource();\n const [latestEntry, setLatestEntry] = useState<Entity | undefined>();\n\n useEffect(() => {\n if (!path || !entityId) return;\n\n const historyPath = `${path}/${entityId}/__history`;\n const unsubscribe = dataSource.listenCollection?.({\n path: historyPath,\n collection,\n orderBy: \"__metadata.updated_on\",\n order: \"desc\",\n limit: 1,\n onUpdate: (entities) => {\n setLatestEntry(entities[0]);\n },\n onError: (error) => {\n console.error(\"Error fetching latest history entry:\", error);\n }\n });\n\n return () => {\n if (typeof unsubscribe === \"function\") {\n unsubscribe();\n }\n };\n }, [path, entityId, dataSource]);\n\n const metadata = latestEntry?.values?.__metadata;\n const uid = metadata?.updated_by;\n const editedOn = metadata?.updated_on;\n\n if (!uid && !editedOn) return null;\n\n const user: User | null | undefined = uid ? getUser?.(uid) : undefined;\n const date = editedOn instanceof Date ? editedOn : (editedOn?.toDate ? editedOn.toDate() : null);\n const timeString = date ? getRelativeTimeString(date) : null;\n\n const displayName = user?.displayName ?? user?.email ?? uid;\n const photoURL = user?.photoURL;\n\n return (\n <div className=\"flex items-center gap-2 text-xs text-text-secondary dark:text-text-secondary-dark\">\n {photoURL ? (\n <img\n src={photoURL}\n alt={displayName ?? \"User\"}\n className=\"rounded-full object-cover w-6 h-6\"\n />\n ) : (\n <div className=\"rounded-full bg-primary/10 dark:bg-primary-dark/20 flex items-center justify-center text-primary dark:text-primary-dark font-medium w-6 h-6 text-xs\">\n {(displayName ?? \"?\").charAt(0).toUpperCase()}\n </div>\n )}\n <span>\n {displayName}{timeString ? ` · ${timeString}` : \"\"}\n </span>\n </div>\n );\n}\n","import React from \"react\";\nimport { PluginFormActionProps } from \"@firecms/core\";\nimport { LastEditedByIndicator } from \"./LastEditedByIndicator\";\n\n/**\n * Renders the \"last edited by\" indicator in the entity form top bar.\n * Used as a plugin `form.ActionsTop` component.\n */\nexport function LastEditedByFormAction({\n entityId,\n path,\n status,\n collection,\n}: PluginFormActionProps) {\n if (status === \"new\" || status === \"copy\" || !entityId) return null;\n if (!collection.history) return null;\n\n return <LastEditedByIndicator\n path={path}\n entityId={entityId}\n collection={collection}\n />;\n}\n","import { useCallback, useMemo } from \"react\";\nimport { EntityCollection, FireCMSPlugin, mergeCallbacks, User } from \"@firecms/core\";\nimport { EntityHistoryView } from \"./components/EntityHistoryView\";\nimport { HistoryIcon } from \"@firecms/ui\";\nimport { entityHistoryCallbacks } from \"./entity_history_callbacks\";\nimport { HistoryControllerProvider } from \"./HistoryControllerProvider\";\nimport { LastEditedByFormAction } from \"./components/LastEditedByPluginComponents\";\n\n/**\n * This plugin adds a history view to the entity side panel.\n */\nexport function useEntityHistoryPlugin(props?: EntityHistoryPluginProps): FireCMSPlugin<any, any, any, EntityHistoryPluginProps> {\n\n const { defaultEnabled = false } = props ?? {};\n\n const modifyCollection = useCallback((collection: EntityCollection) => {\n if (collection.history === true || (defaultEnabled && collection.history !== false)) {\n return {\n ...collection,\n history: true,\n entityViews: [\n ...(collection.entityViews ?? []),\n {\n key: \"__history\",\n name: \"History\",\n tabComponent: <HistoryIcon size={\"small\"} />,\n Builder: EntityHistoryView,\n position: \"start\"\n }\n ],\n callbacks: mergeCallbacks(collection.callbacks, entityHistoryCallbacks)\n } satisfies EntityCollection;\n }\n return collection;\n }, []);\n\n return useMemo(() => ({\n key: \"entity_history\",\n provider: {\n Component: HistoryControllerProvider,\n props: {\n getUser: props?.getUser\n }\n },\n form: {\n BeforeTitle: LastEditedByFormAction\n },\n collection: {\n modifyCollection\n }\n } satisfies FireCMSPlugin), [props]);\n}\n\nexport type EntityHistoryPluginProps = {\n /**\n * If true, the history view will be enabled to all collections by default.\n * Each collection can override this value by setting the `history` property.\n */\n defaultEnabled?: boolean;\n\n /**\n * Function to get the user object from the uid.\n * @param uid\n */\n getUser?: (uid: string) => User | null;\n}\n"],"names":["HistoryControllerContext","React","createContext","useHistoryController","useContext","HistoryControllerProvider","memo","t0","$","_c","children","getUser","t1","t2","equal","UserChip","user","email","uid","displayName","photoURL","jsx","t3","t4","t5","Chip","t6","Tooltip","PreviousValueView","previousValueInPath","childProperty","propertyKey","Typography","Symbol","for","jsxs","PropertyPreview","KeyboardBackspaceIcon","EntityHistoryEntry","actions","hover","collection","collectionProp","previewKeys","onClick","size","entity","previousValues","authController","useAuthController","customizationController","useCustomizationController","navigationController","useNavigationController","sideEntityController","useSideEntityController","getCollection","path","updatedOn","values","Error","updatedBy","resolvedCollection","useMemo","resolveCollection","propertyConfigs","toLocaleString","cls","defaultBorderMixin","IconButton","e","open","entityId","id","allowFullScreen","subcollections","undefined","entityViews","permissions","create","delete","edit","read","updateUrl","KeyboardTabIcon","map","key","getPropertyInPath","properties","valueInPath","getValueInPath","element","SkeletonPropertyComponent","JSON","stringify","EntityHistoryView","formContext","snackbarController","useSnackbarController","dirty","formex","dataSource","useDataSource","pathAndId","revertVersionDialog","setRevertVersionDialog","useState","revisions","setRevisions","isLoading","setIsLoading","hasMore","setHasMore","limit","setLimit","containerRef","useRef","observerRef","loadMoreRef","listener","listenCollection","order","orderBy","startAfter","onUpdate","entities","length","onError","error","console","useEffect","currentContainer","current","currentLoadMore","disconnect","options","root","rootMargin","threshold","handleObserver","entries","target","isIntersecting","_temp","observer","IntersectionObserver","observe","Label","doRevert","revertVersion","revertValues","__metadata","reverted","updated_on","Date","updated_by","saveReverted","saveEntity","status","saveRevertedHistory","Promise","all","then","resetForm","message","type","catch","error_0","t7","t8","t9","Fragment","t10","t11","revision","index","changed_fields","previous_values","HistoryIcon","t12","t13","Boolean","t14","t15","t16","t17","EntityView","t18","ErrorBoundary","ConfirmationDialog","t19","prev","createHistoryEntry","context","changedFields","findChangedFields","entry","debug","entityHistoryCallbacks","onSaveSuccess","props","oldValues","newValues","prefix","allKeys","Set","Object","keys","oldValue","newValue","currentPath","push","Array","isArray","i","nestedChanges","getRelativeTimeString","date","now","diffMs","getTime","diffSeconds","Math","floor","diffMinutes","diffHours","diffDays","toLocaleDateString","LastEditedByIndicator","latestEntry","setLatestEntry","historyPath","unsubscribe","metadata","editedOn","toDate","timeString","charAt","toUpperCase","LastEditedByFormAction","history","useEntityHistoryPlugin","defaultEnabled","modifyCollection","useCallback","name","tabComponent","Builder","position","callbacks","mergeCallbacks","provider","Component","form","BeforeTitle"],"mappings":"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"}
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@firecms/entity_history",
3
3
  "type": "module",
4
- "version": "3.0.1",
4
+ "version": "3.1.0-canary.768c91f",
5
5
  "access": "public",
6
6
  "main": "./dist/index.umd.js",
7
7
  "module": "./dist/index.es.js",
8
8
  "types": "./dist/index.d.ts",
9
9
  "source": "src/index.ts",
10
10
  "dependencies": {
11
- "@firecms/core": "^3.0.1",
12
- "@firecms/formex": "^3.0.1",
13
- "@firecms/ui": "^3.0.1"
11
+ "@firecms/core": "^3.1.0-canary.768c91f",
12
+ "@firecms/formex": "^3.1.0-canary.768c91f",
13
+ "@firecms/ui": "^3.1.0-canary.768c91f"
14
14
  },
15
15
  "peerDependencies": {
16
- "react": ">=18.0.0",
17
- "react-dom": ">=18.0.0"
16
+ "react": ">=18.3.1 || >=19.0.0",
17
+ "react-dom": ">=18.3.1 || >=19.0.0"
18
18
  },
19
19
  "exports": {
20
20
  ".": {
@@ -29,7 +29,7 @@
29
29
  "build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json",
30
30
  "prepublishOnly": "run-s build",
31
31
  "clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f",
32
- "test": "jest"
32
+ "test": "jest --passWithNoTests"
33
33
  },
34
34
  "browserslist": {
35
35
  "production": [
@@ -47,8 +47,8 @@
47
47
  "@jest/globals": "^30.2.0",
48
48
  "@testing-library/jest-dom": "^6.9.1",
49
49
  "@types/jest": "^29.5.14",
50
- "@types/react": "^18.3.24",
51
- "@types/react-dom": "^18.3.7",
50
+ "@types/react": "^19.2.3",
51
+ "@types/react-dom": "^19.2.3",
52
52
  "@vitejs/plugin-react": "^4.7.0",
53
53
  "babel-jest": "^29.7.0",
54
54
  "babel-plugin-react-compiler": "^19.0.0-beta-af1b7da-20250417",
@@ -82,5 +82,5 @@
82
82
  "publishConfig": {
83
83
  "access": "public"
84
84
  },
85
- "gitHead": "72d951d01d15ef5a7efcde6c63839f65964d2f7a"
85
+ "gitHead": "ccfcbd20ff3a6adb3e94edfe4b1a0c2af1f0a492"
86
86
  }
@@ -0,0 +1,93 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { Entity, useDataSource, User } from "@firecms/core";
3
+ import { useHistoryController } from "../HistoryControllerProvider";
4
+
5
+ function getRelativeTimeString(date: Date): string {
6
+ const now = new Date();
7
+ const diffMs = now.getTime() - date.getTime();
8
+ const diffSeconds = Math.floor(diffMs / 1000);
9
+ const diffMinutes = Math.floor(diffSeconds / 60);
10
+ const diffHours = Math.floor(diffMinutes / 60);
11
+ const diffDays = Math.floor(diffHours / 24);
12
+
13
+ if (diffSeconds < 60) return "just now";
14
+ if (diffMinutes < 60) return `${diffMinutes}m ago`;
15
+ if (diffHours < 24) return `${diffHours}h ago`;
16
+ if (diffDays < 30) return `${diffDays}d ago`;
17
+ return date.toLocaleDateString();
18
+ }
19
+
20
+ /**
21
+ * Fetches the latest history entry from the __history subcollection
22
+ * and displays who last edited the entity and when.
23
+ */
24
+ export function LastEditedByIndicator({
25
+ path,
26
+ entityId,
27
+ collection
28
+ }: {
29
+ path: string;
30
+ entityId: string;
31
+ collection: any;
32
+ }) {
33
+ const { getUser } = useHistoryController();
34
+ const dataSource = useDataSource();
35
+ const [latestEntry, setLatestEntry] = useState<Entity | undefined>();
36
+
37
+ useEffect(() => {
38
+ if (!path || !entityId) return;
39
+
40
+ const historyPath = `${path}/${entityId}/__history`;
41
+ const unsubscribe = dataSource.listenCollection?.({
42
+ path: historyPath,
43
+ collection,
44
+ orderBy: "__metadata.updated_on",
45
+ order: "desc",
46
+ limit: 1,
47
+ onUpdate: (entities) => {
48
+ setLatestEntry(entities[0]);
49
+ },
50
+ onError: (error) => {
51
+ console.error("Error fetching latest history entry:", error);
52
+ }
53
+ });
54
+
55
+ return () => {
56
+ if (typeof unsubscribe === "function") {
57
+ unsubscribe();
58
+ }
59
+ };
60
+ }, [path, entityId, dataSource]);
61
+
62
+ const metadata = latestEntry?.values?.__metadata;
63
+ const uid = metadata?.updated_by;
64
+ const editedOn = metadata?.updated_on;
65
+
66
+ if (!uid && !editedOn) return null;
67
+
68
+ const user: User | null | undefined = uid ? getUser?.(uid) : undefined;
69
+ const date = editedOn instanceof Date ? editedOn : (editedOn?.toDate ? editedOn.toDate() : null);
70
+ const timeString = date ? getRelativeTimeString(date) : null;
71
+
72
+ const displayName = user?.displayName ?? user?.email ?? uid;
73
+ const photoURL = user?.photoURL;
74
+
75
+ return (
76
+ <div className="flex items-center gap-2 text-xs text-text-secondary dark:text-text-secondary-dark">
77
+ {photoURL ? (
78
+ <img
79
+ src={photoURL}
80
+ alt={displayName ?? "User"}
81
+ className="rounded-full object-cover w-6 h-6"
82
+ />
83
+ ) : (
84
+ <div className="rounded-full bg-primary/10 dark:bg-primary-dark/20 flex items-center justify-center text-primary dark:text-primary-dark font-medium w-6 h-6 text-xs">
85
+ {(displayName ?? "?").charAt(0).toUpperCase()}
86
+ </div>
87
+ )}
88
+ <span>
89
+ {displayName}{timeString ? ` · ${timeString}` : ""}
90
+ </span>
91
+ </div>
92
+ );
93
+ }
@@ -0,0 +1,23 @@
1
+ import React from "react";
2
+ import { PluginFormActionProps } from "@firecms/core";
3
+ import { LastEditedByIndicator } from "./LastEditedByIndicator";
4
+
5
+ /**
6
+ * Renders the "last edited by" indicator in the entity form top bar.
7
+ * Used as a plugin `form.ActionsTop` component.
8
+ */
9
+ export function LastEditedByFormAction({
10
+ entityId,
11
+ path,
12
+ status,
13
+ collection,
14
+ }: PluginFormActionProps) {
15
+ if (status === "new" || status === "copy" || !entityId) return null;
16
+ if (!collection.history) return null;
17
+
18
+ return <LastEditedByIndicator
19
+ path={path}
20
+ entityId={entityId}
21
+ collection={collection}
22
+ />;
23
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./useEntityHistoryPlugin";
2
2
  export * from "./HistoryControllerProvider";
3
3
  export { createHistoryEntry } from "./entity_history_callbacks";
4
+ export { LastEditedByIndicator } from "./components/LastEditedByIndicator";
4
5
  export * from "./types";
package/src/types.ts CHANGED
@@ -19,4 +19,3 @@ export interface NewHistoryEntryParams<T = any> {
19
19
  entityId: string;
20
20
  collection?: EntityCollection;
21
21
  }
22
-
@@ -4,6 +4,7 @@ import { EntityHistoryView } from "./components/EntityHistoryView";
4
4
  import { HistoryIcon } from "@firecms/ui";
5
5
  import { entityHistoryCallbacks } from "./entity_history_callbacks";
6
6
  import { HistoryControllerProvider } from "./HistoryControllerProvider";
7
+ import { LastEditedByFormAction } from "./components/LastEditedByPluginComponents";
7
8
 
8
9
  /**
9
10
  * This plugin adds a history view to the entity side panel.
@@ -16,12 +17,13 @@ export function useEntityHistoryPlugin(props?: EntityHistoryPluginProps): FireCM
16
17
  if (collection.history === true || (defaultEnabled && collection.history !== false)) {
17
18
  return {
18
19
  ...collection,
20
+ history: true,
19
21
  entityViews: [
20
22
  ...(collection.entityViews ?? []),
21
23
  {
22
24
  key: "__history",
23
25
  name: "History",
24
- tabComponent: <HistoryIcon size={"small"}/>,
26
+ tabComponent: <HistoryIcon size={"small"} />,
25
27
  Builder: EntityHistoryView,
26
28
  position: "start"
27
29
  }
@@ -40,6 +42,9 @@ export function useEntityHistoryPlugin(props?: EntityHistoryPluginProps): FireCM
40
42
  getUser: props?.getUser
41
43
  }
42
44
  },
45
+ form: {
46
+ BeforeTitle: LastEditedByFormAction
47
+ },
43
48
  collection: {
44
49
  modifyCollection
45
50
  }