mirror of
https://github.com/barthuijgen/factorio-sites.git
synced 2025-02-01 13:27:43 +02:00
Add book preview to create blueprint page
This commit is contained in:
parent
c6d0bc8788
commit
d5432c0d5c
@ -1,10 +1,18 @@
|
||||
import { css } from "@emotion/react";
|
||||
import Link from "next/link";
|
||||
import { ChildTreeBlueprintBookEnriched } from "@factorio-sites/web-utils";
|
||||
import Link, { LinkProps } from "next/link";
|
||||
import { FactorioIcon } from "./FactorioIcon";
|
||||
import { FactorioCode } from "./FactorioCode";
|
||||
import { BlueprintBookData, BlueprintStringData, ChildTree, Icon } from "@factorio-sites/types";
|
||||
|
||||
const componentStyles = css`
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
.child-tree-wrapper {
|
||||
height: 480px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.blueprint,
|
||||
.book {
|
||||
display: flex;
|
||||
@ -12,6 +20,7 @@ const componentStyles = css`
|
||||
color: white;
|
||||
padding: 4px 0;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background: #636363;
|
||||
}
|
||||
}
|
||||
@ -24,67 +33,164 @@ const componentStyles = css`
|
||||
}
|
||||
`;
|
||||
|
||||
interface BookChildTreeProps {
|
||||
blueprint_book: ChildTreeBlueprintBookEnriched;
|
||||
base_url: string;
|
||||
selected_id: string;
|
||||
interface BlueprintItem {
|
||||
type: "blueprint";
|
||||
id?: string;
|
||||
name: string;
|
||||
icons: Icon[];
|
||||
}
|
||||
|
||||
export interface BlueprintBookItem {
|
||||
type: "blueprint_book";
|
||||
id?: string;
|
||||
name: string;
|
||||
icons: Icon[];
|
||||
children: TreeItem[];
|
||||
}
|
||||
|
||||
type TreeItem = BlueprintItem | BlueprintBookItem;
|
||||
|
||||
interface BookChildTreeProps {
|
||||
book_item: BlueprintBookItem;
|
||||
base_url?: string;
|
||||
selected_id: string | null;
|
||||
}
|
||||
|
||||
const OptionalLink: React.FC<LinkProps & { include: boolean; className?: string }> = ({
|
||||
include,
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}) => {
|
||||
return include ? (
|
||||
<Link {...props}>
|
||||
<a className={className}>{children}</a>
|
||||
</Link>
|
||||
) : (
|
||||
<div className={className}>{children}</div>
|
||||
);
|
||||
};
|
||||
|
||||
const InnerBookChildTree: React.FC<BookChildTreeProps> = ({ book_item, base_url, selected_id }) => {
|
||||
return (
|
||||
<>
|
||||
<OptionalLink
|
||||
include={Boolean(base_url && book_item.id)}
|
||||
href={`${base_url}?selected=${book_item.id}&type=book`}
|
||||
replace
|
||||
className={"book" + (selected_id === book_item.id ? " active" : "")}
|
||||
>
|
||||
<FactorioIcon type="item" icon="blueprint-book" size={20} />
|
||||
{book_item.icons &&
|
||||
book_item.icons.map((icon, index) => (
|
||||
<FactorioIcon key={index} type={icon.signal.type} icon={icon.signal.name} size={20} />
|
||||
))}
|
||||
<span className="label">
|
||||
<FactorioCode code={book_item.name || ""} />
|
||||
</span>
|
||||
</OptionalLink>
|
||||
<div css={{ marginLeft: `20px` }}>
|
||||
{book_item.children.map((child, index) => {
|
||||
return child.type === "blueprint" ? (
|
||||
<OptionalLink
|
||||
include={Boolean(base_url && child.id)}
|
||||
key={child.id || index}
|
||||
href={`${base_url}?selected=${child.id}`}
|
||||
replace
|
||||
className={"blueprint" + (selected_id === child.id ? " active" : "")}
|
||||
>
|
||||
{child.icons &&
|
||||
child.icons.map((icon, index) => (
|
||||
<FactorioIcon
|
||||
key={index}
|
||||
type={icon.signal.type}
|
||||
icon={icon.signal.name}
|
||||
size={20}
|
||||
/>
|
||||
))}
|
||||
<span className="label">
|
||||
<FactorioCode code={child.name || ""} />
|
||||
</span>
|
||||
</OptionalLink>
|
||||
) : child.type === "blueprint_book" ? (
|
||||
<InnerBookChildTree
|
||||
key={child.id || index}
|
||||
book_item={child}
|
||||
base_url={base_url}
|
||||
selected_id={selected_id}
|
||||
/>
|
||||
) : null;
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const BookChildTree: React.FC<BookChildTreeProps> = ({
|
||||
blueprint_book,
|
||||
book_item,
|
||||
base_url,
|
||||
selected_id,
|
||||
}) => {
|
||||
return (
|
||||
<div css={componentStyles}>
|
||||
<div>
|
||||
<Link href={`${base_url}?selected=${blueprint_book.id}&type=book`} replace>
|
||||
<a className={"book" + (selected_id === blueprint_book.id ? " active" : "")}>
|
||||
<FactorioIcon type="item" icon="blueprint-book" size={20} />
|
||||
{blueprint_book.icons &&
|
||||
blueprint_book.icons.map((icon, index) => (
|
||||
<FactorioIcon
|
||||
key={index}
|
||||
type={icon.signal.type}
|
||||
icon={icon.signal.name}
|
||||
size={20}
|
||||
/>
|
||||
))}
|
||||
<span className="label">
|
||||
<FactorioCode code={blueprint_book.name || ""} />
|
||||
</span>
|
||||
</a>
|
||||
</Link>
|
||||
<div css={{ marginLeft: `20px` }}>
|
||||
{blueprint_book.children.map((child) => {
|
||||
return child.type === "blueprint" ? (
|
||||
<Link key={child.id} href={`${base_url}?selected=${child.id}`} replace>
|
||||
<a className={"blueprint" + (selected_id === child.id ? " active" : "")}>
|
||||
{child.icons &&
|
||||
child.icons.map((icon, index) => (
|
||||
<FactorioIcon
|
||||
key={index}
|
||||
type={icon.signal.type}
|
||||
icon={icon.signal.name}
|
||||
size={20}
|
||||
/>
|
||||
))}
|
||||
<span className="label">
|
||||
<FactorioCode code={child.name || ""} />
|
||||
</span>
|
||||
</a>
|
||||
</Link>
|
||||
) : child.type === "blueprint_book" ? (
|
||||
<BookChildTree
|
||||
key={child.id}
|
||||
blueprint_book={child}
|
||||
base_url={base_url}
|
||||
selected_id={selected_id}
|
||||
/>
|
||||
) : null;
|
||||
})}
|
||||
</div>
|
||||
<div className="child-tree-wrapper ">
|
||||
<InnerBookChildTree book_item={book_item} base_url={base_url} selected_id={selected_id} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const convertBlueprintDataToTree = (data: BlueprintStringData): TreeItem | null => {
|
||||
if (data.blueprint_book) {
|
||||
return convertBlueprintBookDataToTree(data.blueprint_book);
|
||||
}
|
||||
if (data.blueprint) {
|
||||
return {
|
||||
type: "blueprint",
|
||||
name: data.blueprint.label || "",
|
||||
icons: data.blueprint.icons,
|
||||
};
|
||||
}
|
||||
|
||||
// console.warn("convertBlueprintBookDataToTree called without a blueprint or blueprint book", data);
|
||||
return null;
|
||||
};
|
||||
|
||||
export const convertBlueprintBookDataToTree = (data: BlueprintBookData): BlueprintBookItem => {
|
||||
return {
|
||||
type: "blueprint_book",
|
||||
name: data.label,
|
||||
icons: data.icons || [],
|
||||
children: data.blueprints
|
||||
.map(convertBlueprintDataToTree)
|
||||
.filter((x) => x !== null) as TreeItem[],
|
||||
};
|
||||
};
|
||||
|
||||
export const mergeChildTreeWithTreeItem = (
|
||||
tree_item: TreeItem,
|
||||
id: string,
|
||||
child_tree?: ChildTree
|
||||
): TreeItem => {
|
||||
if (tree_item.type === "blueprint") {
|
||||
return { ...tree_item, id };
|
||||
}
|
||||
|
||||
return {
|
||||
...tree_item,
|
||||
id,
|
||||
children: tree_item.children
|
||||
.map((child, index) => {
|
||||
const child_tree_item = child_tree?.[index];
|
||||
if (child_tree_item?.type === "blueprint_book") {
|
||||
return mergeChildTreeWithTreeItem(child, child_tree_item.id, child_tree_item.children);
|
||||
} else if (child_tree_item?.type === "blueprint") {
|
||||
return mergeChildTreeWithTreeItem(child, child_tree_item.id);
|
||||
}
|
||||
|
||||
// console.warn("mergeChildTreeWithTreeItem called with invalid child_tree", child_tree_item);
|
||||
return null;
|
||||
})
|
||||
.filter((x) => x !== null) as TreeItem[],
|
||||
};
|
||||
};
|
||||
|
@ -9,14 +9,15 @@ import {
|
||||
BlueprintPage,
|
||||
BlueprintStringData,
|
||||
} from "@factorio-sites/types";
|
||||
import {
|
||||
chakraResponsive,
|
||||
mergeBlueprintDataAndChildTree,
|
||||
parseBlueprintStringClient,
|
||||
} from "@factorio-sites/web-utils";
|
||||
import { chakraResponsive, parseBlueprintStringClient } from "@factorio-sites/web-utils";
|
||||
import { Panel } from "../../components/Panel";
|
||||
import { Markdown } from "../../components/Markdown";
|
||||
import { BookChildTree } from "../../components/BookChildTree";
|
||||
import {
|
||||
BlueprintBookItem,
|
||||
BookChildTree,
|
||||
convertBlueprintBookDataToTree,
|
||||
mergeChildTreeWithTreeItem,
|
||||
} from "../../components/BookChildTree";
|
||||
import { CopyButton } from "../../components/CopyButton";
|
||||
import { useUrl } from "../../hooks/url.hook";
|
||||
import { FavoriteButton } from "./FavoriteButton";
|
||||
@ -40,17 +41,6 @@ const StyledBlueptintPage = styled(Grid)`
|
||||
margin-right: 0.5rem;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.panel {
|
||||
&.child-tree {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
.child-tree-wrapper {
|
||||
height: 480px;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const descriptionCss = css({
|
||||
@ -111,14 +101,15 @@ export const BlueprintBookSubPage: React.FC<BlueprintBookSubPageProps> = ({
|
||||
}, []);
|
||||
|
||||
const bookChildTreeData = useMemo(() => {
|
||||
if (!mainBookData) return null;
|
||||
return mergeBlueprintDataAndChildTree(mainBookData, {
|
||||
id: blueprint_book.id,
|
||||
name: blueprint_book.label,
|
||||
type: "blueprint_book",
|
||||
children: blueprint_book.child_tree,
|
||||
});
|
||||
}, [blueprint_book.child_tree, blueprint_book.id, blueprint_book.label, mainBookData]);
|
||||
if (mainBookData?.blueprint_book) {
|
||||
return mergeChildTreeWithTreeItem(
|
||||
convertBlueprintBookDataToTree(mainBookData.blueprint_book),
|
||||
blueprint_book.id,
|
||||
blueprint_book.child_tree
|
||||
) as BlueprintBookItem;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [mainBookData]);
|
||||
|
||||
useEffect(() => {
|
||||
fetch(`/api/string/${selectedHash}`)
|
||||
@ -164,7 +155,7 @@ export const BlueprintBookSubPage: React.FC<BlueprintBookSubPageProps> = ({
|
||||
{bookChildTreeData && (
|
||||
<div className="child-tree-wrapper">
|
||||
<BookChildTree
|
||||
blueprint_book={bookChildTreeData}
|
||||
book_item={bookChildTreeData}
|
||||
base_url={`/blueprint/${blueprint_page.id}`}
|
||||
selected_id={selected.data.id}
|
||||
/>
|
||||
|
@ -23,7 +23,6 @@ const handler = apiHandler(async (req, res, { session }) => {
|
||||
const errors: Record<string, string> = {};
|
||||
|
||||
if (!title) errors.title = "Required";
|
||||
if (!description) errors.description = "Required";
|
||||
if (!string) errors.string = "Required";
|
||||
if (!parsed) errors.string = "Not recognised as a blueprint string";
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { NextPage } from "next";
|
||||
import { useRouter } from "next/router";
|
||||
import {
|
||||
@ -10,63 +10,128 @@ import {
|
||||
Box,
|
||||
Text,
|
||||
} from "@chakra-ui/react";
|
||||
import { Formik, Field, FieldProps } from "formik";
|
||||
import { Formik, Field, FieldProps, useFormikContext } from "formik";
|
||||
import { css } from "@emotion/react";
|
||||
import { chakraResponsive } from "@factorio-sites/web-utils";
|
||||
import styled from "@emotion/styled";
|
||||
import { chakraResponsive, parseBlueprintStringClient } from "@factorio-sites/web-utils";
|
||||
import { TAGS } from "@factorio-sites/common-utils";
|
||||
import { Panel } from "../../components/Panel";
|
||||
import { validateCreateBlueprintForm } from "../../utils/validate";
|
||||
import {
|
||||
joinValidations,
|
||||
validateBlueprintString,
|
||||
validateRequired,
|
||||
validateTags,
|
||||
} from "../../utils/validate";
|
||||
import { ImageEditor } from "../../components/ImageEditor";
|
||||
import { Select } from "../../components/Select";
|
||||
import { Button } from "../../components/Button";
|
||||
import { MDEditor } from "../../components/MDEditor";
|
||||
import { pageHandler } from "../../utils/page-handler";
|
||||
import { BlueprintStringData } from "@factorio-sites/types";
|
||||
import { BookChildTree, convertBlueprintBookDataToTree } from "../../components/BookChildTree";
|
||||
import { Tooltip } from "../../components/Tooltip";
|
||||
import { Markdown } from "../../components/Markdown";
|
||||
|
||||
const FieldStyle = css`
|
||||
margin-bottom: 1rem;
|
||||
`;
|
||||
|
||||
export const UserBlueprintCreate: NextPage = () => {
|
||||
const router = useRouter();
|
||||
const StyledMarkdown = styled(Markdown)`
|
||||
max-height: 400px;
|
||||
margin-bottom: 1rem;
|
||||
border: 1px solid rgb(226, 232, 240);
|
||||
border-radius: 4px;
|
||||
padding: 0.5rem 1rem;
|
||||
`;
|
||||
|
||||
interface FormValues {
|
||||
title: string;
|
||||
description: string;
|
||||
string: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
const FormContent: React.FC = () => {
|
||||
const {
|
||||
values,
|
||||
handleSubmit,
|
||||
setFieldValue,
|
||||
isSubmitting,
|
||||
status,
|
||||
} = useFormikContext<FormValues>();
|
||||
|
||||
const [blueprintData, setBlueprintData] = useState<BlueprintStringData | null>(null);
|
||||
const [step, setStep] = useState(0);
|
||||
|
||||
const tagsOptions = TAGS.map((tag) => ({
|
||||
label: `${tag.category}: ${tag.label}`,
|
||||
value: tag.value,
|
||||
}));
|
||||
|
||||
const description =
|
||||
blueprintData?.blueprint?.description || blueprintData?.blueprint_book?.description || "";
|
||||
|
||||
useEffect(() => {
|
||||
if (values.string) {
|
||||
const data = parseBlueprintStringClient(values.string);
|
||||
if (data) {
|
||||
setBlueprintData(data);
|
||||
setFieldValue("title", data.blueprint?.label || data.blueprint_book?.label || "");
|
||||
return;
|
||||
}
|
||||
}
|
||||
setBlueprintData(null);
|
||||
}, [values.string, setFieldValue]);
|
||||
|
||||
const onClickNext = () => {
|
||||
if (blueprintData) {
|
||||
setStep(1);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Formik
|
||||
initialValues={{ title: "", description: "", string: "", tags: [] }}
|
||||
validate={validateCreateBlueprintForm}
|
||||
onSubmit={async (values, { setSubmitting, setErrors, setStatus }) => {
|
||||
setStatus("");
|
||||
|
||||
const result = await fetch("/api/blueprint/create", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(values),
|
||||
}).then((res) => res.json());
|
||||
|
||||
if (result.status) {
|
||||
setSubmitting(false);
|
||||
setStatus(result.status);
|
||||
} else if (result.errors) {
|
||||
setSubmitting(false);
|
||||
setErrors(result.errors);
|
||||
} else if (result.success) {
|
||||
router.push(`/blueprint/${result.id}`);
|
||||
}
|
||||
}}
|
||||
<SimpleGrid
|
||||
columns={2}
|
||||
gap={6}
|
||||
templateColumns={chakraResponsive({ mobile: "1fr", desktop: "1fr 1fr" })}
|
||||
>
|
||||
{({ isSubmitting, handleSubmit, status, values, errors, setFieldValue }) => (
|
||||
<SimpleGrid
|
||||
columns={2}
|
||||
gap={6}
|
||||
templateColumns={chakraResponsive({ mobile: "1fr", desktop: "1fr 1fr" })}
|
||||
>
|
||||
<Panel title="Create new blueprint">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Field name="title">
|
||||
<Panel title="Create new blueprint">
|
||||
<form onSubmit={handleSubmit}>
|
||||
{step === 0 ? (
|
||||
<>
|
||||
<Field
|
||||
name="string"
|
||||
validate={joinValidations(validateRequired, validateBlueprintString)}
|
||||
>
|
||||
{({ field, meta }: FieldProps) => (
|
||||
<FormControl
|
||||
id="string"
|
||||
isRequired
|
||||
isInvalid={meta.touched && !!meta.error}
|
||||
css={FieldStyle}
|
||||
>
|
||||
<FormLabel>Blueprint string</FormLabel>
|
||||
<Input type="text" {...field} />
|
||||
<FormErrorMessage>{meta.error}</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
<Box css={{ display: "flex", alignItems: "center" }}>
|
||||
<Button
|
||||
primary
|
||||
type="button"
|
||||
css={{ marginRight: "1rem" }}
|
||||
disabled={!blueprintData}
|
||||
onClick={onClickNext}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
{status && <Text css={{ marginLeft: "1rem", color: "red" }}>{status}</Text>}
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Field name="title" validate={validateRequired}>
|
||||
{({ field, meta }: FieldProps) => (
|
||||
<FormControl
|
||||
id="title"
|
||||
@ -89,7 +154,10 @@ export const UserBlueprintCreate: NextPage = () => {
|
||||
isInvalid={meta.touched && !!meta.error}
|
||||
css={FieldStyle}
|
||||
>
|
||||
<FormLabel>Description</FormLabel>
|
||||
<FormLabel>
|
||||
Description{" "}
|
||||
<Tooltip text="This description is shown next to the description already stored in the blueprint." />
|
||||
</FormLabel>
|
||||
<MDEditor
|
||||
value={field.value}
|
||||
onChange={(value) => setFieldValue("description", value)}
|
||||
@ -99,13 +167,20 @@ export const UserBlueprintCreate: NextPage = () => {
|
||||
)}
|
||||
</Field>
|
||||
|
||||
<Field name="tags">
|
||||
{description && (
|
||||
<>
|
||||
<div css={{ marginBottom: "0.5rem" }}>Blueprint description</div>
|
||||
<StyledMarkdown>{description}</StyledMarkdown>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Field name="tags" validate={validateTags}>
|
||||
{({ field, meta }: FieldProps) => (
|
||||
<FormControl id="tags" isInvalid={meta.touched && !!meta.error} css={FieldStyle}>
|
||||
<FormLabel>Tags</FormLabel>
|
||||
<Select
|
||||
options={tagsOptions}
|
||||
value={field.value}
|
||||
value={field.value || []}
|
||||
onChange={(tags) => setFieldValue("tags", tags)}
|
||||
/>
|
||||
<FormErrorMessage>{meta.error}</FormErrorMessage>
|
||||
@ -113,38 +188,62 @@ export const UserBlueprintCreate: NextPage = () => {
|
||||
)}
|
||||
</Field>
|
||||
|
||||
<Field name="string">
|
||||
{({ field, meta }: FieldProps) => (
|
||||
<FormControl
|
||||
id="string"
|
||||
isRequired
|
||||
isInvalid={meta.touched && !!meta.error}
|
||||
css={FieldStyle}
|
||||
>
|
||||
<FormLabel>Blueprint string</FormLabel>
|
||||
<Input type="text" {...field} />
|
||||
<FormErrorMessage>{meta.error}</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
<Box css={{ display: "flex", alignItems: "center" }}>
|
||||
<Button type="button" css={{ marginRight: "1rem" }} onClick={() => setStep(0)}>
|
||||
Previus
|
||||
</Button>
|
||||
<Button primary type="submit" disabled={isSubmitting}>
|
||||
Submit
|
||||
</Button>
|
||||
{status && <Text css={{ marginLeft: "1rem", color: "red" }}>{status}</Text>}
|
||||
</Box>
|
||||
</form>
|
||||
</Panel>
|
||||
<Panel title="Preview">
|
||||
<Box>
|
||||
{values.string && !errors.string && (
|
||||
<ImageEditor string={values.string}></ImageEditor>
|
||||
)}
|
||||
</Box>
|
||||
</Panel>
|
||||
</SimpleGrid>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</form>
|
||||
</Panel>
|
||||
<Panel title="Preview">
|
||||
<Box>
|
||||
{blueprintData?.blueprint_book ? (
|
||||
<BookChildTree
|
||||
book_item={convertBlueprintBookDataToTree(blueprintData.blueprint_book)}
|
||||
selected_id={null}
|
||||
/>
|
||||
) : values.string ? (
|
||||
<ImageEditor string={values.string} />
|
||||
) : null}
|
||||
</Box>
|
||||
</Panel>
|
||||
</SimpleGrid>
|
||||
);
|
||||
};
|
||||
|
||||
export const UserBlueprintCreate: NextPage = () => {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<Formik
|
||||
initialValues={{ title: "", description: "", string: "", tags: [] }}
|
||||
onSubmit={async (values, { setSubmitting, setErrors, setStatus }) => {
|
||||
setStatus("");
|
||||
|
||||
const result = await fetch("/api/blueprint/create", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(values),
|
||||
}).then((res) => res.json());
|
||||
|
||||
if (result.status) {
|
||||
setSubmitting(false);
|
||||
setStatus(result.status);
|
||||
} else if (result.errors) {
|
||||
setSubmitting(false);
|
||||
setErrors(result.errors);
|
||||
} else if (result.success) {
|
||||
router.push(`/blueprint/${result.id}`);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FormContent />
|
||||
</Formik>
|
||||
);
|
||||
};
|
||||
|
@ -27,6 +27,10 @@ export const UserBlueprints: NextPage<UserBlueprintsProps> = ({ blueprints: blue
|
||||
if (!blueprints) return null;
|
||||
|
||||
const deleteBlueprint = async (id: string) => {
|
||||
if (!window.confirm("Are you sure you want to delete this blueprint?")) {
|
||||
return;
|
||||
}
|
||||
|
||||
setDeleteId(id);
|
||||
try {
|
||||
await fetch(`/api/blueprint/delete/${id}`, { method: "DELETE" });
|
||||
|
@ -15,6 +15,12 @@ export const validateRequired = (value: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const validateTags = (value: string[]) => {
|
||||
if (!Array.isArray(value)) {
|
||||
return "Invalid value";
|
||||
}
|
||||
};
|
||||
|
||||
const filterUndefined = (errors: Record<string, string | undefined>): Record<string, string> => {
|
||||
return Object.keys(errors)
|
||||
.filter((key) => errors[key] !== undefined)
|
||||
@ -75,41 +81,28 @@ export const validateUserForm = (auth: AuthContextProps) => (values: UserFormVal
|
||||
return filterUndefined(errors);
|
||||
};
|
||||
|
||||
interface CreateBlueprintValues {
|
||||
title: string;
|
||||
description: string;
|
||||
string: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
export const validateCreateBlueprintForm = (values: CreateBlueprintValues) => {
|
||||
const errors = {} as Record<keyof CreateBlueprintValues, string | undefined>;
|
||||
errors.title = validateRequired(values.title);
|
||||
errors.string = validateRequired(values.string);
|
||||
|
||||
if (values.string) {
|
||||
console.log(parseBlueprintStringClient(values.string));
|
||||
export const joinValidations = <T>(...validations: Array<(value: T) => string | undefined>) => (
|
||||
value: T
|
||||
): string | undefined => {
|
||||
for (let i = 0; i < validations.length; i++) {
|
||||
const error = validations[i](value);
|
||||
if (error) return error;
|
||||
}
|
||||
|
||||
// If string is set also validate it to be parsable
|
||||
if (!errors.string && !parseBlueprintStringClient(values.string)) {
|
||||
errors.string = "Not recognised as a blueprint string";
|
||||
}
|
||||
|
||||
if (!Array.isArray(values.tags)) {
|
||||
errors.tags = "Invalid tags value";
|
||||
}
|
||||
|
||||
return filterUndefined(errors);
|
||||
};
|
||||
|
||||
export const validateBlueprintString = (value: string) => {
|
||||
if (value) {
|
||||
const parsed = parseBlueprintStringClient(value);
|
||||
console.log(parsed);
|
||||
console.log({ parsed });
|
||||
|
||||
if (!parsed) {
|
||||
return "Not recognised as a blueprint string";
|
||||
}
|
||||
|
||||
if (!parsed.blueprint && !parsed.blueprint_book) {
|
||||
return "Must have a blueprint or blueprint book";
|
||||
}
|
||||
} else {
|
||||
return "Not recognised as a blueprint string";
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Signal } from "./blueprint-string";
|
||||
|
||||
export interface ChildTreeBlueprint {
|
||||
type: "blueprint";
|
||||
id: string;
|
||||
|
Loading…
x
Reference in New Issue
Block a user