@rebasepro/app 0.20.1-canary.g4d882ca → 0.21.0

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.
@@ -1,15 +1,4 @@
1
1
  import type { RebaseProps } from "./RebaseProps.js";
2
2
  import React from "react";
3
3
  import { User } from "@rebasepro/types";
4
- /**
5
- * If you are using independent components of the admin
6
- * you need to wrap them with this main component, so the internal hooks work.
7
- *
8
- * This is the main component of Rebase. It acts as the provider of all the
9
- * internal contexts and hooks.
10
- *
11
- * You only need to use this component if you are building a custom app.
12
- *
13
- * @group Core
14
- */
15
4
  export declare function Rebase<USER extends User, DB = unknown>(props: RebaseProps<USER, DB>): React.JSX.Element;
@@ -220,7 +220,11 @@ export type RebaseProps<USER extends User, DB = unknown> = {
220
220
  databaseAdmin?: DatabaseAdmin;
221
221
  /**
222
222
  * Use this controller to access the configuration that is stored locally,
223
- * and not defined in code
223
+ * and not defined in code.
224
+ *
225
+ * Optional: when omitted the panel builds a localStorage-backed store of
226
+ * its own, so column widths, column order and the view mode a collection
227
+ * was left in survive without the host app wiring anything.
224
228
  */
225
229
  userConfigPersistence?: UserConfigurationPersistence;
226
230
  /**
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ import { type ChipColorKey } from "@rebasepro/ui";
2
3
  interface TaskEntity {
3
4
  id: string;
4
5
  values: {
@@ -44,7 +45,7 @@ export interface TaskTableProps {
44
45
  }
45
46
  export declare const resolutionDisplay: Record<string, {
46
47
  label: string;
47
- colorScheme: string;
48
+ colorScheme: ChipColorKey;
48
49
  }>;
49
50
  export declare function TaskTable({ tasks, loading, pendingTasks, visiblePendingTasks, hasMorePending, onLoadMorePending, pendingCount, togglingIds, onToggleTask, onOpenTask, onOpenClient, clientsMap, stageLabels, recentlyToggledIds, completedTasks, completedLoaded, loadingCompleted, completedHasMore, onExpandCompleted, onLoadMoreCompleted }: TaskTableProps): React.JSX.Element;
50
51
  export {};
package/dist/debug.js CHANGED
@@ -892,7 +892,7 @@ import {
892
892
  import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
893
893
  var resolutionDisplay = {
894
894
  verified: { label: "Verified", colorScheme: "green" },
895
- needs_followup: { label: "Needs Follow-up", colorScheme: "amber" },
895
+ needs_followup: { label: "Needs Follow-up", colorScheme: "yellow" },
896
896
  suitable: { label: "Suitable", colorScheme: "green" },
897
897
  not_a_fit: { label: "Not a Fit", colorScheme: "red" },
898
898
  response_received: { label: "Response Received", colorScheme: "green" },
@@ -903,13 +903,13 @@ var resolutionDisplay = {
903
903
  signed: { label: "Signed", colorScheme: "green" },
904
904
  reminder_sent: { label: "Reminder Sent", colorScheme: "blue" },
905
905
  payment_confirmed: { label: "Payment Confirmed", colorScheme: "green" },
906
- not_yet_received: { label: "Not Yet Received", colorScheme: "amber" },
906
+ not_yet_received: { label: "Not Yet Received", colorScheme: "yellow" },
907
907
  sent: { label: "Sent", colorScheme: "green" },
908
908
  confirmed: { label: "Confirmed", colorScheme: "green" },
909
- not_yet: { label: "Not Yet", colorScheme: "amber" },
909
+ not_yet: { label: "Not Yet", colorScheme: "yellow" },
910
910
  scheduled: { label: "Scheduled", colorScheme: "green" },
911
911
  feedback_received: { label: "Feedback Received", colorScheme: "green" },
912
- awaiting_response: { label: "Awaiting Response", colorScheme: "amber" },
912
+ awaiting_response: { label: "Awaiting Response", colorScheme: "yellow" },
913
913
  archived: { label: "Archived", colorScheme: "green" },
914
914
  done: { label: "Done", colorScheme: "green" }
915
915
  };
@@ -1,18 +1,3 @@
1
1
  import type { SlotName, SlotRegistry } from "@rebasepro/cms-types";
2
2
  import React from "react";
3
- /**
4
- * Hook that retrieves and renders all slot contributions for a given slot name.
5
- *
6
- * @param slot - The slot name to render contributions for.
7
- * @param props - Props passed to each slot component.
8
- * @returns An array of rendered React nodes, each wrapped in an ErrorBoundary.
9
- *
10
- * @example
11
- * ```tsx
12
- * const actions = useSlot("home.actions", { context });
13
- * return <div>{actions}</div>;
14
- * ```
15
- *
16
- * @group Hooks
17
- */
18
3
  export declare function useSlot<K extends SlotName>(slot: K, props: SlotRegistry[K]): React.ReactNode[];
package/dist/index.es.js CHANGED
@@ -1971,22 +1971,26 @@ function useBrowserTitleAndIcon(name, logo) {
1971
1971
  *
1972
1972
  * @group Hooks
1973
1973
  */
1974
+ /**
1975
+ * Are these two prop objects the same, one level down?
1976
+ *
1977
+ * Its own function so the comparison can be typed on `object` rather than on
1978
+ * the slot's props. `SlotRegistry[K]` is a union of interfaces, and an
1979
+ * interface has no implicit index signature, so reading it as
1980
+ * `Record<string, unknown>` — which the loop this replaces did, to both
1981
+ * arguments — is a conversion tsc refuses and `as unknown as` was suppressing.
1982
+ * `Object.keys` needs no such claim.
1983
+ */
1984
+ function shallowEqual(a, b) {
1985
+ if (a === b) return true;
1986
+ const keys = Object.keys(a);
1987
+ if (keys.length !== Object.keys(b).length) return false;
1988
+ return keys.every((key) => a[key] === b[key]);
1989
+ }
1974
1990
  function useSlot(slot, props) {
1975
1991
  const { resolvedSlots } = useCustomizationController();
1976
1992
  const propsRef = React.useRef(props);
1977
- const currentProps = props;
1978
- const prevProps = propsRef.current;
1979
- let changed = false;
1980
- if (currentProps !== prevProps) {
1981
- const keys = Object.keys(currentProps);
1982
- const prevKeys = Object.keys(prevProps);
1983
- if (keys.length !== prevKeys.length) changed = true;
1984
- else for (let i = 0; i < keys.length; i++) if (currentProps[keys[i]] !== prevProps[keys[i]]) {
1985
- changed = true;
1986
- break;
1987
- }
1988
- }
1989
- if (changed) propsRef.current = props;
1993
+ if (!shallowEqual(props, propsRef.current)) propsRef.current = props;
1990
1994
  const stableProps = propsRef.current;
1991
1995
  return useMemo(() => {
1992
1996
  return resolvedSlots.filter((s) => s.slot === slot).sort((a, b) => (a.order ?? 50) - (b.order ?? 50)).map((s, i) => {
@@ -2017,12 +2021,13 @@ function useBuildLocalConfigurationPersistence() {
2017
2021
  if (configCache.current[storageKey]) return configCache.current[storageKey];
2018
2022
  return getCollectionFromStorage(storageKey);
2019
2023
  }, [getCollectionFromStorage]);
2024
+ const [configVersion, setConfigVersion] = useState(0);
2020
2025
  const onCollectionModified = useCallback((path, data) => {
2021
2026
  const storageKey = `collection_config::${stripCollectionPath(path)}`;
2022
- writeStoredJson(storageKey, data);
2023
- const cachedConfig = configCache.current[storageKey];
2024
- const newConfig = mergeDeep(cachedConfig ?? getCollectionFromStorage(storageKey), data);
2025
- configCache.current[storageKey] = mergeDeep(configCache.current[storageKey], newConfig);
2027
+ const merged = mergeDeep(configCache.current[storageKey] ?? getCollectionFromStorage(storageKey), data);
2028
+ configCache.current[storageKey] = merged;
2029
+ writeStoredJson(storageKey, merged);
2030
+ setConfigVersion((version) => version + 1);
2026
2031
  }, [getCollectionFromStorage]);
2027
2032
  const [recentlyVisitedPaths, _setRecentlyVisitedPaths] = useState([]);
2028
2033
  const [favouritePaths, _setFavouritePaths] = useState([]);
@@ -2058,6 +2063,7 @@ function useBuildLocalConfigurationPersistence() {
2058
2063
  collapsedGroups,
2059
2064
  setCollapsedGroups
2060
2065
  }), [
2066
+ configVersion,
2061
2067
  onCollectionModified,
2062
2068
  getCollectionConfig,
2063
2069
  recentlyVisitedPaths,
@@ -5876,6 +5882,20 @@ var en = {
5876
5882
  some_entities_deleted: "Some of the entities have been deleted, but not all",
5877
5883
  error_deleting_entities: "Error deleting entities",
5878
5884
  deleted: "Deleted",
5885
+ selection_options: "Selection options",
5886
+ selection_menu_all_loaded: "All on this page",
5887
+ selection_menu_all_matching: "All {{total}} {{collection}}",
5888
+ selection_menu_none: "None",
5889
+ selection_select_all_loaded: "Select all loaded rows",
5890
+ selection_deselect_all: "Deselect all",
5891
+ selection_reading_rows: "Reading rows… {{loaded}} of {{total}}",
5892
+ selection_reading_rows_unknown: "Reading rows… {{loaded}} so far",
5893
+ selection_deleting_progress: "Deleted {{completed}} of {{total}}",
5894
+ confirm_delete_selection: "Delete {{total}} {{collection}}?",
5895
+ confirm_delete_selection_unknown: "Delete every row matching the current filter?",
5896
+ confirm_delete_selection_body: "This cannot be undone. Each row is deleted individually, so this may take a while.",
5897
+ export_selection_count: "Download the {{total}} selected rows as a {{format}}",
5898
+ export_selection_all_matching: "Download every {{collection}} matching the current filter",
5879
5899
  select_reference: "Select reference",
5880
5900
  select_references: "Select references",
5881
5901
  account_settings: "Account Settings",
@@ -6874,6 +6894,20 @@ var es = {
6874
6894
  some_entities_deleted: "Algunas entidades han sido eliminadas, pero no todas",
6875
6895
  error_deleting_entities: "Error al eliminar entidades",
6876
6896
  deleted: "eliminado",
6897
+ selection_options: "Opciones de selección",
6898
+ selection_menu_all_loaded: "Todo en esta página",
6899
+ selection_menu_all_matching: "Los {{total}} {{collection}}",
6900
+ selection_menu_none: "Ninguno",
6901
+ selection_select_all_loaded: "Seleccionar las filas cargadas",
6902
+ selection_deselect_all: "Deseleccionar todo",
6903
+ selection_reading_rows: "Leyendo filas… {{loaded}} de {{total}}",
6904
+ selection_reading_rows_unknown: "Leyendo filas… {{loaded}} hasta ahora",
6905
+ selection_deleting_progress: "Eliminados {{completed}} de {{total}}",
6906
+ confirm_delete_selection: "¿Eliminar {{total}} {{collection}}?",
6907
+ confirm_delete_selection_unknown: "¿Eliminar todas las filas que coinciden con el filtro actual?",
6908
+ confirm_delete_selection_body: "Esta acción no se puede deshacer. Cada fila se elimina por separado, así que puede tardar un rato.",
6909
+ export_selection_count: "Descargar las {{total}} filas seleccionadas en {{format}}",
6910
+ export_selection_all_matching: "Descargar todos los {{collection}} que coinciden con el filtro actual",
6877
6911
  select_reference: "Seleccionar referencia",
6878
6912
  select_references: "Seleccionar referencias",
6879
6913
  account_settings: "Configuración de la cuenta",
@@ -7864,6 +7898,20 @@ var de = {
7864
7898
  some_entities_deleted: "Einige der Entitäten wurden gelöscht, aber nicht alle",
7865
7899
  error_deleting_entities: "Fehler beim Löschen der Entitäten",
7866
7900
  deleted: "gelöscht",
7901
+ selection_options: "Auswahloptionen",
7902
+ selection_menu_all_loaded: "Alle auf dieser Seite",
7903
+ selection_menu_all_matching: "Alle {{total}} {{collection}}",
7904
+ selection_menu_none: "Keine",
7905
+ selection_select_all_loaded: "Geladene Zeilen auswählen",
7906
+ selection_deselect_all: "Alle abwählen",
7907
+ selection_reading_rows: "Zeilen werden gelesen… {{loaded}} von {{total}}",
7908
+ selection_reading_rows_unknown: "Zeilen werden gelesen… bisher {{loaded}}",
7909
+ selection_deleting_progress: "{{completed}} von {{total}} gelöscht",
7910
+ confirm_delete_selection: "{{total}} {{collection}} löschen?",
7911
+ confirm_delete_selection_unknown: "Alle Zeilen löschen, die dem aktuellen Filter entsprechen?",
7912
+ confirm_delete_selection_body: "Dies kann nicht rückgängig gemacht werden. Jede Zeile wird einzeln gelöscht, das kann eine Weile dauern.",
7913
+ export_selection_count: "Die {{total}} ausgewählten Zeilen als {{format}} herunterladen",
7914
+ export_selection_all_matching: "Alle {{collection}}, die dem aktuellen Filter entsprechen, herunterladen",
7867
7915
  select_reference: "Referenz auswählen",
7868
7916
  select_references: "Referenzen auswählen",
7869
7917
  account_settings: "Kontoeinstellungen",
@@ -8854,6 +8902,20 @@ var fr = {
8854
8902
  some_entities_deleted: "Certaines des entités ont été supprimées, mais pas toutes",
8855
8903
  error_deleting_entities: "Erreur lors de la suppression des entités",
8856
8904
  deleted: "supprimé",
8905
+ selection_options: "Options de sélection",
8906
+ selection_menu_all_loaded: "Tout sur cette page",
8907
+ selection_menu_all_matching: "Les {{total}} {{collection}}",
8908
+ selection_menu_none: "Aucun",
8909
+ selection_select_all_loaded: "Sélectionner les lignes chargées",
8910
+ selection_deselect_all: "Tout désélectionner",
8911
+ selection_reading_rows: "Lecture des lignes… {{loaded}} sur {{total}}",
8912
+ selection_reading_rows_unknown: "Lecture des lignes… {{loaded}} jusqu’à présent",
8913
+ selection_deleting_progress: "{{completed}} supprimés sur {{total}}",
8914
+ confirm_delete_selection: "Supprimer {{total}} {{collection}} ?",
8915
+ confirm_delete_selection_unknown: "Supprimer toutes les lignes correspondant au filtre actuel ?",
8916
+ confirm_delete_selection_body: "Cette action est irréversible. Chaque ligne est supprimée séparément, l’opération peut donc prendre du temps.",
8917
+ export_selection_count: "Télécharger les {{total}} lignes sélectionnées au format {{format}}",
8918
+ export_selection_all_matching: "Télécharger tous les {{collection}} correspondant au filtre actuel",
8857
8919
  select_reference: "Sélectionner une référence",
8858
8920
  select_references: "Sélectionner des références",
8859
8921
  account_settings: "Paramètres du compte",
@@ -9844,6 +9906,20 @@ var it = {
9844
9906
  some_entities_deleted: "Alcune entità sono state eliminate, ma non tutte",
9845
9907
  error_deleting_entities: "Errore durante l'eliminazione delle entità",
9846
9908
  deleted: "eliminata",
9909
+ selection_options: "Opzioni di selezione",
9910
+ selection_menu_all_loaded: "Tutto in questa pagina",
9911
+ selection_menu_all_matching: "Tutti i {{total}} {{collection}}",
9912
+ selection_menu_none: "Nessuno",
9913
+ selection_select_all_loaded: "Seleziona le righe caricate",
9914
+ selection_deselect_all: "Deseleziona tutto",
9915
+ selection_reading_rows: "Lettura delle righe… {{loaded}} di {{total}}",
9916
+ selection_reading_rows_unknown: "Lettura delle righe… {{loaded}} finora",
9917
+ selection_deleting_progress: "Eliminati {{completed}} di {{total}}",
9918
+ confirm_delete_selection: "Eliminare {{total}} {{collection}}?",
9919
+ confirm_delete_selection_unknown: "Eliminare tutte le righe che corrispondono al filtro attuale?",
9920
+ confirm_delete_selection_body: "L’operazione non può essere annullata. Ogni riga viene eliminata singolarmente, quindi potrebbe richiedere del tempo.",
9921
+ export_selection_count: "Scarica le {{total}} righe selezionate in formato {{format}}",
9922
+ export_selection_all_matching: "Scarica tutti i {{collection}} che corrispondono al filtro attuale",
9847
9923
  select_reference: "Seleziona riferimento",
9848
9924
  select_references: "Seleziona riferimenti",
9849
9925
  account_settings: "Impostazioni account",
@@ -10834,6 +10910,20 @@ var hi = {
10834
10910
  some_entities_deleted: "कुछ संस्थाएँ हटा दी गई हैं, लेकिन सभी नहीं",
10835
10911
  error_deleting_entities: "संस्थाओं को हटाने में त्रुटि",
10836
10912
  deleted: "हटाया गया",
10913
+ selection_options: "चयन विकल्प",
10914
+ selection_menu_all_loaded: "इस पृष्ठ पर सभी",
10915
+ selection_menu_all_matching: "सभी {{total}} {{collection}}",
10916
+ selection_menu_none: "कोई नहीं",
10917
+ selection_select_all_loaded: "लोड की गई पंक्तियाँ चुनें",
10918
+ selection_deselect_all: "सभी अचयनित करें",
10919
+ selection_reading_rows: "पंक्तियाँ पढ़ी जा रही हैं… {{total}} में से {{loaded}}",
10920
+ selection_reading_rows_unknown: "पंक्तियाँ पढ़ी जा रही हैं… अब तक {{loaded}}",
10921
+ selection_deleting_progress: "{{total}} में से {{completed}} हटाए गए",
10922
+ confirm_delete_selection: "क्या {{total}} {{collection}} हटाएँ?",
10923
+ confirm_delete_selection_unknown: "क्या वर्तमान फ़िल्टर से मेल खाने वाली सभी पंक्तियाँ हटाएँ?",
10924
+ confirm_delete_selection_body: "इसे पूर्ववत नहीं किया जा सकता। हर पंक्ति अलग-अलग हटाई जाती है, इसलिए इसमें कुछ समय लग सकता है।",
10925
+ export_selection_count: "{{total}} चयनित पंक्तियाँ {{format}} रूप में डाउनलोड करें",
10926
+ export_selection_all_matching: "वर्तमान फ़िल्टर से मेल खाने वाले सभी {{collection}} डाउनलोड करें",
10837
10927
  select_reference: "संदर्भ चुनें",
10838
10928
  select_references: "संदर्भ चुनें (एकाधिक)",
10839
10929
  account_settings: "खाता सेटिंग्स",
@@ -11829,6 +11919,20 @@ var pt = {
11829
11919
  some_entities_deleted: "Algumas entidades foram excluídas, mas não todas",
11830
11920
  error_deleting_entities: "Erro ao excluir entidades",
11831
11921
  deleted: "excluído(a)",
11922
+ selection_options: "Opções de seleção",
11923
+ selection_menu_all_loaded: "Tudo nesta página",
11924
+ selection_menu_all_matching: "Todos os {{total}} {{collection}}",
11925
+ selection_menu_none: "Nenhum",
11926
+ selection_select_all_loaded: "Selecionar as linhas carregadas",
11927
+ selection_deselect_all: "Desmarcar tudo",
11928
+ selection_reading_rows: "A ler linhas… {{loaded}} de {{total}}",
11929
+ selection_reading_rows_unknown: "A ler linhas… {{loaded}} até agora",
11930
+ selection_deleting_progress: "Eliminados {{completed}} de {{total}}",
11931
+ confirm_delete_selection: "Eliminar {{total}} {{collection}}?",
11932
+ confirm_delete_selection_unknown: "Eliminar todas as linhas que correspondem ao filtro atual?",
11933
+ confirm_delete_selection_body: "Esta ação não pode ser anulada. Cada linha é eliminada individualmente, pelo que pode demorar algum tempo.",
11934
+ export_selection_count: "Descarregar as {{total}} linhas selecionadas em {{format}}",
11935
+ export_selection_all_matching: "Descarregar todos os {{collection}} que correspondem ao filtro atual",
11832
11936
  select_reference: "Selecionar referência",
11833
11937
  select_references: "Selecionar referências",
11834
11938
  account_settings: "Definições da conta",
@@ -12486,6 +12590,21 @@ function buildResources(translations) {
12486
12590
  *
12487
12591
  * @group Core
12488
12592
  */
12593
+ /**
12594
+ * Does this socket carry the admin surface?
12595
+ *
12596
+ * `RebaseWebSocket` declares none of `DatabaseAdmin`'s methods — the SQL console
12597
+ * is served over the same socket only when the backend enables it — so the
12598
+ * question is genuinely a runtime one. It used to be asked as
12599
+ * `typeof (ws as unknown as Record<string, unknown>).executeSql === "function"`
12600
+ * and then answered a second time, one line down, by asserting the whole socket
12601
+ * to `DatabaseAdmin`: a check and a claim, with nothing connecting them. A
12602
+ * predicate makes the check *be* the narrowing, so the five method reads below
12603
+ * are checked against the interface they came from.
12604
+ */
12605
+ function canExecuteSql(ws) {
12606
+ return typeof ws.executeSql === "function";
12607
+ }
12489
12608
  function Rebase(props) {
12490
12609
  const { children, entityLinkBuilder, userConfigPersistence, dateTimeFormat, locale, client, authController: authControllerProp, storageSource: storageSourceProp, dataSources: dataSourcesProp, storageSources: storageSourcesProp, databaseAdmin, plugins: pluginsProp, slots: directSlots = [], onAnalyticsEvent, propertyConfigs, entityViews, collectionViews, entityActions, effectiveRoleController, apiUrl, translations, components: componentsProp } = props;
12491
12610
  const plugins = pluginsProp;
@@ -12493,6 +12612,8 @@ function Rebase(props) {
12493
12612
  const keys = plugins.map((p) => p.key);
12494
12613
  if (new Set(keys).size !== keys.length) console.error("Duplicate plugin keys detected:", keys.filter((k, i) => keys.indexOf(k) !== i));
12495
12614
  }
12615
+ const localUserConfigPersistence = useBuildLocalConfigurationPersistence();
12616
+ const resolvedUserConfigPersistence = userConfigPersistence ?? localUserConfigPersistence;
12496
12617
  const resolvedSlots = useMemo(() => [...directSlots, ...(plugins ?? []).flatMap((p) => p.slots ?? [])], [directSlots, plugins]);
12497
12618
  useEffect(() => {
12498
12619
  const dead = new Set(UNRENDERED_SLOTS);
@@ -12618,7 +12739,7 @@ function Rebase(props) {
12618
12739
  const defaultDriver = normalizedDataSources.find((d) => d.key === DEFAULT_DATA_SOURCE_KEY && d.driver)?.driver ?? (!client && normalizedDataSources.length === 1 ? normalizedDataSources[0].driver : void 0);
12619
12740
  if (defaultDriver?.admin) return defaultDriver.admin;
12620
12741
  const ws = client?.ws;
12621
- if (ws && typeof ws.executeSql === "function") {
12742
+ if (ws && canExecuteSql(ws)) {
12622
12743
  const wsAdmin = ws;
12623
12744
  return {
12624
12745
  executeSql: wsAdmin.executeSql.bind(wsAdmin),
@@ -12694,7 +12815,7 @@ function Rebase(props) {
12694
12815
  children: /* @__PURE__ */ jsx(CustomizationControllerContext.Provider, {
12695
12816
  value: customizationController,
12696
12817
  children: /* @__PURE__ */ jsx(UserConfigurationPersistenceContext.Provider, {
12697
- value: userConfigPersistence,
12818
+ value: resolvedUserConfigPersistence,
12698
12819
  children: /* @__PURE__ */ jsx(StorageSourcesContext.Provider, {
12699
12820
  value: storageSourcesValue,
12700
12821
  children: /* @__PURE__ */ jsx(StorageSourceContext.Provider, {