import { importShared } from '../../__federation_fn_import-BLFDCY-V.js';
import { j as jsxRuntimeExports } from '../../jsx-runtime-XI9uIe3W.js';
import { F as FeatureUtils, M as ModelUtils, s as withPlaceholderSlots, c as createFieldMapSpec, l as createPluginSpec } from '../../model-utils-BfcjfzCn.js';
import { c as create } from '../../react-CL9M_wyN.js';
import { u as useFormState, a as useForm } from '../../index.esm-BraHxHLB.js';
import { A as isEqual } from '../../isEqual-tnzoadW8.js';

const {Alert,AlertDescription,AlertTitle,Badge,Button,Dialog,DialogContent,DialogHeader,DialogTitle,DialogTrigger,JsonDiffViewer} = await importShared('@baseplate-dev/ui-components');

const CHANGE_TYPE_BADGE_VARIANT = {
    added: 'default',
    updated: 'secondary',
    removed: 'destructive',
};
function DefinitionDiffEntryRow({ entry, }) {
    return (jsxRuntimeExports.jsxs("div", { className: "flex w-full items-center justify-between rounded-md px-3 py-2 text-sm", children: [jsxRuntimeExports.jsx("span", { className: "font-medium", children: entry.label }), jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntimeExports.jsx(Badge, { variant: CHANGE_TYPE_BADGE_VARIANT[entry.type], children: entry.type }), jsxRuntimeExports.jsxs(Dialog, { children: [jsxRuntimeExports.jsx(DialogTrigger, { asChild: true, children: jsxRuntimeExports.jsx(Button, { variant: "ghost", size: "sm", children: "Details" }) }), jsxRuntimeExports.jsxs(DialogContent, { width: "xl", children: [jsxRuntimeExports.jsx(DialogHeader, { children: jsxRuntimeExports.jsxs(DialogTitle, { children: [entry.label, " \u2014 ", entry.type] }) }), jsxRuntimeExports.jsx("div", { className: "max-h-[60vh] overflow-auto", children: entry.type === 'added' ? (jsxRuntimeExports.jsx("pre", { className: "whitespace-pre-wrap text-xs", children: JSON.stringify(entry.merged, null, 2) })) : entry.type === 'removed' ? (jsxRuntimeExports.jsx("pre", { className: "whitespace-pre-wrap text-xs", children: JSON.stringify(entry.current, null, 2) })) : (jsxRuntimeExports.jsx(JsonDiffViewer, { oldValue: entry.current, newValue: entry.merged })) })] })] })] })] }));
}
function DefinitionDiffAlert({ diff, upToDateMessage = 'All changes are up to date. No changes needed.', }) {
    if (!diff.hasChanges) {
        return (jsxRuntimeExports.jsxs(Alert, { variant: "default", children: [jsxRuntimeExports.jsx(AlertTitle, { children: "Up to Date" }), jsxRuntimeExports.jsx(AlertDescription, { children: upToDateMessage })] }));
    }
    return (jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [jsxRuntimeExports.jsxs(Alert, { variant: "warning", children: [jsxRuntimeExports.jsx(AlertTitle, { children: "Pending Changes" }), jsxRuntimeExports.jsx(AlertDescription, { children: "The following changes will be applied when you save:" })] }), jsxRuntimeExports.jsx("div", { className: "space-y-1", children: diff.entries.map((entry) => (jsxRuntimeExports.jsx(DefinitionDiffEntryRow, { entry: entry }, `${entry.path}-${entry.label}`))) })] }));
}

const React = await importShared('react');

const ProjectDefinitionContext = React.createContext(null);
function useProjectDefinition() {
    const result = React.useContext(ProjectDefinitionContext);
    if (!result) {
        throw new Error(`useProjectDefinition must be used within a <ProjectDefinitionProvider>`);
    }
    return result;
}

const {ComboboxField: ComboboxField$1,useControllerMerged: useControllerMerged$1} = await importShared('@baseplate-dev/ui-components');

const {useMemo: useMemo$2,useState: useState$1} = await importShared('react');
function createCreateOption$1(value) {
    return {
        label: `Create "${value}"`,
        value,
    };
}
function FeatureComboboxField({ canCreate, value, ...rest }) {
    const { definition } = useProjectDefinition();
    const [searchQuery, setSearchQuery] = useState$1('');
    const featureOptions = useMemo$2(() => {
        const baseFeatures = definition.features.map((feature) => ({
            label: feature.name,
            value: feature.id,
        }));
        if (!canCreate)
            return baseFeatures;
        const newFeatureName = searchQuery ? searchQuery : value;
        const doesNewFeatureExist = baseFeatures.some((option) => option.label === newFeatureName || option.value === newFeatureName);
        return !doesNewFeatureExist &&
            newFeatureName &&
            FeatureUtils.validateFeatureName(newFeatureName)
            ? [...baseFeatures, createCreateOption$1(newFeatureName)]
            : baseFeatures;
    }, [definition.features, searchQuery, value, canCreate]);
    return (jsxRuntimeExports.jsx(ComboboxField$1, { placeholder: "Select a feature", ...rest, searchQuery: canCreate ? searchQuery : undefined, onSearchQueryChange: setSearchQuery, options: featureOptions, value: value }));
}
function FeatureComboboxFieldController({ name, control, ...rest }) {
    const { field, fieldState: { error }, } = useControllerMerged$1({ name, control }, rest);
    const restProps = rest;
    return (jsxRuntimeExports.jsx(FeatureComboboxField, { error: error?.message, ...restProps, ...field, value: field.value ?? null }));
}

const {ComboboxField,useControllerMerged} = await importShared('@baseplate-dev/ui-components');

const {useMemo: useMemo$1,useState} = await importShared('react');
function createCreateOption(value) {
    return {
        label: `Create "${value}"`,
        value,
    };
}
function ModelComboboxField({ canCreate, value, ...rest }) {
    const { definition } = useProjectDefinition();
    const [searchQuery, setSearchQuery] = useState('');
    const modelOptions = useMemo$1(() => {
        const baseModels = definition.models.map((model) => ({
            label: model.name,
            value: model.id,
        }));
        if (!canCreate)
            return baseModels;
        const newModelName = searchQuery ? searchQuery : value;
        const doesNewModelExist = baseModels.some((option) => option.label === newModelName || option.value === newModelName);
        return !doesNewModelExist &&
            newModelName &&
            ModelUtils.validateModelName(newModelName)
            ? [...baseModels, createCreateOption(newModelName)]
            : baseModels;
    }, [definition.models, searchQuery, value, canCreate]);
    return (jsxRuntimeExports.jsx(ComboboxField, { placeholder: "Select a model", ...rest, searchQuery: canCreate ? searchQuery : undefined, onSearchQueryChange: setSearchQuery, options: modelOptions, value: value }));
}
function ModelComboboxFieldController({ name, control, ...rest }) {
    const { field, fieldState: { error }, } = useControllerMerged({ name, control }, rest);
    const restProps = rest;
    return (jsxRuntimeExports.jsx(ModelComboboxField, { error: error?.message, ...restProps, ...field, value: field.value ?? null }));
}

const {createContext} = await importShared('react');

const ErrorHandlerContext = createContext(null);

const {useEffect: useEffect$2,useId} = await importShared('react');
const useBlockerDialogState = create((set) => ({
    activeBlockers: [],
    addBlocker: (options) => {
        set((state) => ({
            activeBlockers: [...state.activeBlockers, options],
        }));
    },
    removeBlocker: (id) => {
        set((state) => ({
            activeBlockers: state.activeBlockers.filter((blocker) => blocker.id !== id),
        }));
    },
    requestedBlockers: [],
    requestBlocker(options) {
        set((state) => ({
            requestedBlockers: [...state.requestedBlockers, options],
        }));
    },
    clearRequestedBlockers: () => {
        set(() => ({
            requestedBlockers: [],
        }));
    },
}));
function useBlockBeforeContinue() {
    const hasActiveBlockers = useBlockerDialogState((state) => state.activeBlockers.length > 0);
    const clearRequestedBlockers = useBlockerDialogState((state) => state.clearRequestedBlockers);
    // make sure we clear any blocker requests if we navigate away
    useEffect$2(() => () => {
        clearRequestedBlockers();
    }, [clearRequestedBlockers]);
    const requestBlocker = useBlockerDialogState((state) => state.requestBlocker);
    // if no blockers, continue immediately
    if (!hasActiveBlockers) {
        return ({ onContinue }) => {
            onContinue();
        };
    }
    return requestBlocker;
}
function useBlockerDialog(options) {
    const id = useId();
    const addBlocker = useBlockerDialogState((state) => state.addBlocker);
    const removeBlocker = useBlockerDialogState((state) => state.removeBlocker);
    useEffect$2(() => {
        if (options.disableBlock) {
            return;
        }
        addBlocker({
            disableBlock: options.disableBlock,
            title: options.title,
            content: options.content,
            buttonContinueWithoutSaveText: options.buttonContinueWithoutSaveText,
            onContinueWithoutSave: options.onContinueWithoutSave,
            buttonContinueText: options.buttonContinueText,
            onContinue: options.onContinue,
            id,
        });
        return () => {
            removeBlocker(id);
        };
    }, [
        addBlocker,
        removeBlocker,
        options.disableBlock,
        options.buttonContinueWithoutSaveText,
        options.onContinueWithoutSave,
        options.buttonContinueText,
        options.content,
        options.title,
        options.onContinue,
        id,
    ]);
}

function useBlockDirtyFormNavigate(formState, reset) {
    useBlockerDialog({
        disableBlock: !formState.isDirty,
        title: 'Unsaved Changes',
        content: 'You have unsaved changes. Are you sure you want to continue?',
        buttonContinueText: 'Discard Changes',
        onContinue: () => {
            reset();
            return true;
        },
    });
}

const {toast: toast$1} = await importShared('@baseplate-dev/ui-components');
const {useEffect: useEffect$1,useRef: useRef$1} = await importShared('react');
function useBlockUnsavedChangesNavigate({ control, reset, onSubmit }) {
  const isDirtyRef = useRef$1(false);
  const formState = useFormState({ control });
  isDirtyRef.current = formState.isDirty;
  useBlockerDialog({
    disableBlock: !formState.isDirty,
    title: "Unsaved Changes",
    content: "You have unsaved changes. Do you want to save your changes?",
    buttonContinueWithoutSaveText: "Discard Changes",
    onContinueWithoutSave: () => {
      reset();
      return true;
    },
    buttonContinueText: "Save",
    onContinue: async () => {
      await onSubmit();
      await new Promise((resolve) => setTimeout(resolve, 1));
      return !isDirtyRef.current;
    }
  });
}

const {useMemo} = await importShared('react');
function useDefinitionSchema(schemaCreator) {
    const { definitionSchemaParserContext } = useProjectDefinition();
    return useMemo(() => {
        if ('slotDefinition' in schemaCreator) {
            return withPlaceholderSlots(schemaCreator)(definitionSchemaParserContext);
        }
        return schemaCreator(definitionSchemaParserContext);
    }, [definitionSchemaParserContext, schemaCreator]);
}

const {useContext} = await importShared('react');
function useErrorHandler() {
    const context = useContext(ErrorHandlerContext);
    if (!context) {
        throw new Error('useErrorHandler must be used within an ErrorHandlerContext');
    }
    return context;
}

const {toast} = await importShared('@baseplate-dev/ui-components');
const {useEffect,useRef} = await importShared('react');
function useResettableForm(props) {
    const formProps = useForm(props);
    const { reset, formState } = formProps;
    const { isDirty } = formState;
    const { updatedExternally } = useProjectDefinition();
    const oldValues = useRef(undefined);
    useEffect(() => {
        if (oldValues.current &&
            !isEqual(oldValues.current.oldDefaultValues, props?.defaultValues)) {
            reset(props?.defaultValues);
            if (isDirty && updatedExternally) {
                toast.warning('Contents were updated externally so form was reset!');
            }
        }
        if (!oldValues.current ||
            !isEqual(oldValues.current.oldDefaultValues, props?.defaultValues)) {
            oldValues.current = {
                oldDefaultValues: props?.defaultValues,
            };
        }
    }, [props?.defaultValues, reset, updatedExternally, isDirty]);
    return formProps;
}

function createAdminCrudActionWebConfig(config) {
    return config;
}
/**
 * Spec for registering admin CRUD action web configs
 */
const adminCrudActionWebSpec = createFieldMapSpec('core/admin-crud-action-web', (t) => ({
    // eslint-disable-next-line @typescript-eslint/no-explicit-any -- plugins register varying action types at runtime
    actions: t.namedArrayToMap(),
}));

function createAdminCrudColumnWebConfig(config) {
    return config;
}
/**
 * Spec for registering admin CRUD column web configs
 */
const adminCrudColumnWebSpec = createFieldMapSpec('core/admin-crud-column-web', (t) => ({
    // eslint-disable-next-line @typescript-eslint/no-explicit-any -- plugins register varying column types at runtime
    columns: t.namedArrayToMap(),
}));

function createAdminCrudInputWebConfig(config) {
    return config;
}
/**
 * Spec for registering admin CRUD input web configs
 */
const adminCrudInputWebSpec = createFieldMapSpec('core/admin-crud-input-web', (t) => ({
    // eslint-disable-next-line @typescript-eslint/no-explicit-any -- plugins register varying input types at runtime
    inputs: t.namedArrayToMap(),
}));

/**
 * Spec for registering navigation builder functions for definition entity types.
 *
 * Use `register(entityType, builder)` during initialization. If the entity type
 * has a parent (THasParent = true), parentId and parentKey are required strings
 * in the builder's params. grandparentId/grandparentKey are always optional and
 * available for grandchild entity types.
 *
 * After initialization, use `getNavBuilder(entityType)` to retrieve the builder.
 */
const entityTypeUrlWebSpec = createPluginSpec('core/entity-type-url-web', {
    initializer: () => {
        const builders = new Map();
        return {
            init: {
                register: (entityType, builder) => {
                    builders.set(entityType.name, builder);
                },
            },
            use: () => ({
                getNavBuilder: (entityType) => builders.get(entityType.name),
            }),
        };
    },
});

function createModelTransformerWebConfig(config) {
    return config;
}
/**
 * Spec for adding web config for transformers
 */
const modelTransformerWebSpec = createFieldMapSpec('core/model-transformer-web', (t) => ({
    // eslint-disable-next-line @typescript-eslint/no-explicit-any -- plugins register varying transformer types at runtime
    transformers: t.namedArrayToMap(),
}), {
    use: (values) => ({
        getWebConfigOrThrow(name) {
            const transformer = values.transformers.get(name);
            if (!transformer) {
                throw new Error(`Transformer ${name} not found`);
            }
            return transformer;
        },
        getWebConfigs: () => [...values.transformers.values()],
    }),
});

function setUndefinedIfEmpty(value) {
    if (value === '')
        return undefined;
    return value;
}

export { DefinitionDiffAlert, ErrorHandlerContext, FeatureComboboxField, FeatureComboboxFieldController, ModelComboboxField, ModelComboboxFieldController, ProjectDefinitionContext, adminCrudActionWebSpec, adminCrudColumnWebSpec, adminCrudInputWebSpec, createAdminCrudActionWebConfig, createAdminCrudColumnWebConfig, createAdminCrudInputWebConfig, createModelTransformerWebConfig, entityTypeUrlWebSpec, modelTransformerWebSpec, setUndefinedIfEmpty, useBlockBeforeContinue, useBlockDirtyFormNavigate, useBlockUnsavedChangesNavigate, useBlockerDialog, useBlockerDialogState, useDefinitionSchema, useErrorHandler, useProjectDefinition, useResettableForm };
//# sourceMappingURL=web-bRU6KpPk.js.map
