import * as React from "react"; import slugify from "@sindresorhus/slugify"; import { Check, ArrowBack, KeyboardArrowDown } from "@rimble/icons"; import { Flash, Link, Box, Button, Flex, Text } from "rimble-ui"; import { mutate } from "swr"; import Editor from "react-simple-code-editor"; import Prism from "prismjs"; import { useDebounce } from "use-debounce"; import { links } from "../../../../config"; import { SchemaDataInput, CompletedSchema, baseWorkingSchema, WorkingSchema } from "../types"; import { jsonSchemaToSchemaInput, isFullSchema, ensureFullSchema } from "../utils"; import { AttributesStep } from "./AttributesStep"; import { ConfirmStep } from "./ConfirmStep"; import { InfoStep } from "./InfoStep"; import { H3 } from "../../../layouts"; import { colors } from "../../../../themes"; import { SertoUiContext, SertoUiContextInterface } from "../../../../context/SertoUiContext"; import { SchemaDetail } from "../SchemaDetail"; import { Popup, PopupGroup } from "../../../elements/Popup/Popup"; import { PrismHighlightedCodeWrap } from "../../../elements/HighlightedJson/HighlightedJson"; import { convertToPascalCase } from "../../../../utils/helpers"; import { v4 as uuidv4 } from 'uuid'; const STEPS = ["INFO", "ATTRIBUTES", "CONFIRM", "DONE"]; export interface CreateSchemaProps { isUpdate?: boolean; userOwnsSchema?: boolean; initialSchemaState?: WorkingSchema; className?: string; onComplete?(): void; onSchemaUpdate?(schema: WorkingSchema): void; onSchemaSaved?(schema: SchemaDataInput): void; progress:boolean; error?: boolean; } export const CreateSchema: React.FunctionComponent = (props) => { const { className, isUpdate, userOwnsSchema, initialSchemaState, onComplete, onSchemaUpdate, onSchemaSaved,progress} = props; const [currentStep, setCurrentStep] = React.useState(STEPS[0]); const [loading, setLoading] = React.useState(false); const [error, setError] = React.useState(""); const [schema, setSchema] = React.useState(baseWorkingSchema); const [debouncedSchema] = useDebounce(schema, 500); const [builtSchema, setBuiltSchema] = React.useState(); const [inputMode, setInputMode] = React.useState<"UI" | "JSON">("UI"); const [inputJson, setInputJson] = React.useState(""); const [debouncedInputSchemaJson] = useDebounce(inputJson, 500); const [inputJsonError, setInputJsonError] = React.useState(""); const schemasService = React.useContext(SertoUiContext).schemasService; const uniqueSlugId = uuidv4() React.useEffect(() => { console.log("schemasService",schemasService) if (initialSchemaState) { if(!userOwnsSchema){ const schemaUnique = {...initialSchemaState, $metadata: { ...initialSchemaState.$metadata, slug: initialSchemaState?.$metadata?.slug+"-"+ uniqueSlugId }}; setSchema(schemaUnique); } else{ setSchema(initialSchemaState); } } else { setSchema(baseWorkingSchema); } }, [initialSchemaState]); React.useEffect(() => { if (!debouncedSchema?.$metadata?.slug && debouncedSchema?.title) { setSchema((oldSchema) => ({ ...oldSchema, $metadata: { ...oldSchema.$metadata, slug: slugify(debouncedSchema?.title || ""), }, })); } try { setInputJsonError(""); setBuiltSchema(jsonSchemaToSchemaInput(debouncedSchema as CompletedSchema, schemasService.buildSchemaUrl)); } catch (err) { console.error("Failed to build schema. Schema:", debouncedSchema, "Error:", err); setInputJsonError("Failed to build schema from JSON: " + err.toString()); } }, [debouncedSchema, schemasService.buildSchemaUrl]); React.useEffect(() => { if (inputMode === "JSON" && builtSchema) { setInputJson(JSON.stringify(JSON.parse(builtSchema.jsonSchema), null, 2)); setInputJsonError(""); } // Hook relies on `builtSchema` but would be huge waste to run this hook every time that changes. We only need to run it when input mode changes so we can populate the JSON textarea with the current state of the schema. So: // eslint-disable-next-line react-hooks/exhaustive-deps }, [inputMode]); React.useEffect(() => { if (!debouncedInputSchemaJson) { return; } try { setInputJsonError(""); const parsedSchema = JSON.parse(debouncedInputSchemaJson); const fullSchema = ensureFullSchema(parsedSchema, schemasService.buildSchemaUrl); if (!isFullSchema(parsedSchema)) { // We've transformed the JSON that they put in the JSON editor, so let's update the JSON editor setInputJson(JSON.stringify(fullSchema, null, 2)); } setSchema(fullSchema); } catch (err) { setInputJsonError(err.toString()); console.warn("Error creating schema from JSON", err); } }, [debouncedInputSchemaJson, schemasService.buildSchemaUrl]); React.useEffect(() => { onSchemaUpdate?.(debouncedSchema); }, [debouncedSchema, onSchemaUpdate]); function updateSchema(updates: Partial) { const newSchema = { ...schema, ...updates, }; if (updates.title) { const subjectLdTerm = convertToPascalCase(updates.title); const credLdTerm = subjectLdTerm + "Credential"; newSchema.$linkedData = { term: credLdTerm, "@id": credLdTerm }; if (newSchema.properties?.credentialSubject) { newSchema.properties.credentialSubject.$linkedData = { term: subjectLdTerm, "@id": subjectLdTerm }; } } setSchema(newSchema); } function goBack() { setError(""); setCurrentStep(STEPS[STEPS.indexOf(currentStep) - 1]); } async function goForward() { setError(""); const nextStep = STEPS[STEPS.indexOf(currentStep) + 1]; if (nextStep === "DONE") { setLoading(true); try { if (!builtSchema) { throw Error("Failed to build schema"); } // await publishSchema(); // setCurrentStep(nextStep); onSchemaSaved?.(builtSchema); } catch (err) { console.error(`Failed to ${isUpdate && userOwnsSchema ? "update" : "create"} schema:`, err); setError(err.toString()); } setLoading(false); } else { setCurrentStep(nextStep); } } async function publishSchema() { onSchemaUpdate?.(schema); if (!builtSchema) { throw Error("Failed to build schema"); } if (isUpdate && userOwnsSchema) { await schemasService.updateSchema(builtSchema); } else { await schemasService.createSchema(builtSchema); } mutate(["getSchemas", true]); if (schema.discoverable) { mutate(["getSchemas", false]); } } // if (currentStep === "DONE") { // return ( // // // // // //

Schema {isUpdate ? "Updated" : "Published"}

//
// // // //
// ); // } return ( {currentStep === "INFO" ? (isUpdate ? (userOwnsSchema ? "Update" : "Fork") : "Create New") + " Schema" : currentStep === "ATTRIBUTES" ? (isUpdate ? "Edit" : "Define") + " Schema Attributes" : "Review Schema"} {currentStep !== "CONFIRM" && ( Edit and preview in the next panel )} {["UI", "JSON"].map((inputName: any) => ( setInputMode(inputName)} className={inputMode === inputName ? "selected" : ""} > {inputName} Editor ))} } > <> {inputMode} Editor {inputMode === "JSON" ? ( <> Warning: This feature is for advanced users, and it is possible to create an unusable schema. Please check the preview on the right to ensure that your schema is as intended. The expected format is JSON Schema{" "} with embedded JSON-LD data . View examples at the{" "} Schema Playground , or start with the UI editor and then adjust as needed. Prism.highlight(inputJson, Prism.languages.json, "json")} style={{ minHeight: "100%" }} /> {inputJsonError && ( Error creating schema from JSON: {inputJsonError} )} ) : ( <> {currentStep !== STEPS[0] && ( {" "} Back )} {currentStep === "INFO" ? ( ) : currentStep === "ATTRIBUTES" ? ( ) : builtSchema ? ( ) : ( <> )} )} {/* {builtSchema && ( )} */} ); };