import { View, Text, StyleSheet, ScrollView } from "react-native"; import { Query, useQueryClient } from "@tanstack/react-query"; import { useSafeAreaInsets } from "@react-buoy/shared-ui"; import Explorer from "./query-browser/Explorer"; import QueryDetails from "./query-browser/QueryDetails"; import ActionButton from "./query-browser/ActionButton"; import { getQueryStatusLabel } from "../utils/getQueryStatusLabel"; import { useActionButtons } from "../hooks/useActionButtons"; import { macOSColors } from "@react-buoy/shared-ui"; import { DataViewer } from "@react-buoy/shared-ui/dataViewer"; interface ActionButtonConfig { label: string; bgColorClass: | "btnRefetch" | "btnTriggerLoading" | "btnTriggerLoadiError" | "btnRemove"; textColorClass: | "btnRefetch" | "btnTriggerLoading" | "btnTriggerLoadiError" | "btnRemove"; disabled: boolean; onPress: () => void; } interface DataEditorModeProps { selectedQuery: Query; isFloatingMode: boolean; disableInternalFooter?: boolean; /** Version number that increments when query state changes, used to force re-renders */ queryVersion?: number; } /** * Primary data editing surface displayed inside the React Query modal. Couples the editable * explorer, read-only insight panels, and action footer for the selected query. */ export function DataEditorMode({ selectedQuery, isFloatingMode, disableInternalFooter = false, queryVersion, }: DataEditorModeProps) { const insets = useSafeAreaInsets({ minBottom: 16 }); const queryClient = useQueryClient(); const actionButtons = useActionButtons(selectedQuery, queryClient, queryVersion); return ( <> {/* Data Explorer Section - Moved to top for immediate data editing */} {/* Query Details Section */} {/* Query Explorer Section - Non-editable viewer */} Query Explorer {/* Action Footer with Safe Area (internal, optional) */} {!disableInternalFooter && ( {actionButtons.map((action: ActionButtonConfig, index: number) => ( ))} )} ); } // External footer component for sticky modal footer usage /** * Standalone footer variant that can be composed into sticky modal layouts while reusing the * standard action button arrangement. */ export function DataEditorActionsFooter({ selectedQuery, isFloatingMode, queryVersion, }: { selectedQuery: Query; isFloatingMode: boolean; /** Version number that increments when query state changes, used to force re-renders */ queryVersion?: number; }) { const insets = useSafeAreaInsets({ minBottom: 16 }); const queryClient = useQueryClient(); const actionButtons = useActionButtons(selectedQuery, queryClient, queryVersion); return ( {actionButtons.map((action: ActionButtonConfig, index: number) => ( ))} ); } function DataExplorer({ visible, selectedQuery, queryVersion = 0, }: { visible: boolean; selectedQuery: Query; queryVersion?: number; }) { if (!visible) return null; return ( Data Editor ); } function DataEmptyState({ visible, selectedQuery, }: { visible: boolean; selectedQuery: Query; }) { if (!visible) return null; const getEmptyStateContent = () => { // Check if query is disabled first if (selectedQuery.isDisabled()) { return { title: "Query Disabled", description: "This query is disabled and won't automatically fetch. Enable the query or manually trigger a fetch to load data.", }; } if ( selectedQuery.state.status === "pending" || getQueryStatusLabel(selectedQuery) === "fetching" ) { return { title: selectedQuery.state.status === "pending" ? "Loading..." : "Refetching...", description: "Please wait while the query is being executed.", }; } if (selectedQuery.state.status === "error") { return { title: "Query Error", description: selectedQuery.state.error?.message || "An error occurred while fetching data.", }; } return { title: "No Data Available", description: "This query has no data to edit. Try refetching the query first.", }; }; const { title, description } = getEmptyStateContent(); return ( {title} {description} ); } const styles = StyleSheet.create({ // Explorer section explorerScrollContainer: { flex: 1, }, explorerScrollContent: { paddingBottom: 16, paddingHorizontal: 8, flexGrow: 1, }, // Section layout matching QueryInformation section: { marginBottom: 16, }, // Empty states matching main dev tools emptyState: { flex: 1, justifyContent: "center", alignItems: "center", padding: 32, }, emptyTitle: { color: macOSColors.text.primary, fontSize: 18, fontWeight: "600", marginBottom: 8, textAlign: "center", }, emptyDescription: { color: macOSColors.text.secondary, fontSize: 14, textAlign: "center", lineHeight: 20, maxWidth: 280, }, // Action footer matching main dev tools exactly actionFooter: { borderTopWidth: 1, borderTopColor: macOSColors.border.default, paddingTop: 4, paddingHorizontal: 12, backgroundColor: macOSColors.background.base, borderBottomLeftRadius: 14, borderBottomRightRadius: 14, }, actionsGrid: { flexDirection: "row", flexWrap: "wrap", gap: 6, // Reduced from 8 justifyContent: "space-between", paddingBottom: 2, }, // Query Explorer styled container matching QueryDetails queryExplorerContainer: { minWidth: 200, backgroundColor: macOSColors.background.card, borderRadius: 6, borderWidth: 1, borderColor: macOSColors.semantic.info + "4D", overflow: "hidden", shadowColor: macOSColors.semantic.info, shadowOffset: { width: 0, height: 0 }, shadowOpacity: 0.1, shadowRadius: 6, }, queryExplorerHeader: { backgroundColor: macOSColors.semantic.infoBackground, paddingHorizontal: 12, paddingVertical: 10, fontWeight: "600", fontSize: 12, color: macOSColors.semantic.info, borderBottomWidth: 1, borderBottomColor: macOSColors.semantic.info + "33", letterSpacing: 0.5, textTransform: "uppercase", fontFamily: "monospace", }, queryExplorerContent: { padding: 8, }, // Data section with green accent - editable/success theme dataContainer: { minWidth: 200, backgroundColor: macOSColors.background.card, borderRadius: 6, borderWidth: 1, borderColor: macOSColors.semantic.info + "4D", overflow: "hidden", shadowColor: macOSColors.semantic.info, shadowOffset: { width: 0, height: 0 }, shadowOpacity: 0.1, shadowRadius: 6, marginTop: 8, }, dataHeader: { backgroundColor: macOSColors.semantic.infoBackground, paddingHorizontal: 12, paddingVertical: 10, fontWeight: "600", fontSize: 12, color: macOSColors.semantic.info, borderBottomWidth: 1, borderBottomColor: macOSColors.semantic.info + "33", letterSpacing: 0.5, textTransform: "uppercase", fontFamily: "monospace", }, dataContent: { padding: 8, }, });