@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.
- package/dist/components/LastEditedByIndicator.d.ts +9 -0
- package/dist/components/LastEditedByPluginComponents.d.ts +6 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +161 -3
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +160 -2
- package/dist/index.umd.js.map +1 -1
- package/package.json +10 -10
- package/src/components/LastEditedByIndicator.tsx +93 -0
- package/src/components/LastEditedByPluginComponents.tsx +23 -0
- package/src/index.ts +1 -0
- package/src/types.ts +0 -1
- package/src/useEntityHistoryPlugin.tsx +6 -1
|
@@ -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
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,
|
|
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
|
package/dist/index.es.js.map
CHANGED
|
@@ -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;
|
package/dist/index.umd.js.map
CHANGED
|
@@ -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.
|
|
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.
|
|
12
|
-
"@firecms/formex": "^3.0.
|
|
13
|
-
"@firecms/ui": "^3.0.
|
|
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": "^
|
|
51
|
-
"@types/react-dom": "^
|
|
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": "
|
|
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
package/src/types.ts
CHANGED
|
@@ -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
|
}
|