tsondb 0.5.8 → 0.5.12
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.
- package/dist/src/bin/tsondb.d.ts +1 -9
- package/dist/src/bin/tsondb.js +7 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/node/index.d.ts +2 -2
- package/dist/src/node/index.js +8 -2
- package/dist/src/node/renderers/jsonschema/index.d.ts +1 -1
- package/dist/src/node/renderers/ts/index.d.ts +1 -1
- package/dist/src/node/{Schema.d.ts → schema/Schema.d.ts} +2 -2
- package/dist/src/node/{Schema.js → schema/Schema.js} +10 -7
- package/dist/src/node/schema/declarations/EntityDecl.d.ts +31 -30
- package/dist/src/node/schema/declarations/EntityDecl.js +1 -0
- package/dist/src/node/server/api/declarations.js +3 -4
- package/dist/src/node/server/api/git.js +1 -2
- package/dist/src/node/server/api/instances.js +2 -3
- package/dist/src/node/server/index.d.ts +7 -2
- package/dist/src/node/server/index.js +10 -10
- package/dist/src/node/server/init.d.ts +1 -1
- package/dist/src/node/server/init.js +10 -0
- package/dist/src/node/utils/displayName.d.ts +3 -0
- package/dist/src/node/utils/displayName.js +19 -0
- package/dist/src/shared/config.d.ts +11 -0
- package/dist/src/{node/renderers/Output.d.ts → shared/output.d.ts} +1 -1
- package/dist/src/shared/output.js +1 -0
- package/dist/src/shared/utils/compare.js +6 -1
- package/dist/src/shared/utils/displayName.d.ts +1 -1
- package/dist/src/shared/utils/displayName.js +1 -1
- package/dist/src/shared/utils/instances.d.ts +3 -2
- package/dist/src/shared/utils/instances.js +3 -3
- package/dist/src/shared/utils/markdown.js +1 -1
- package/dist/src/web/components/Git.js +1 -1
- package/dist/src/web/components/typeInputs/ArrayTypeInput.d.ts +2 -1
- package/dist/src/web/components/typeInputs/ArrayTypeInput.js +10 -6
- package/dist/src/web/components/typeInputs/BooleanTypeInput.d.ts +1 -1
- package/dist/src/web/components/typeInputs/BooleanTypeInput.js +4 -0
- package/dist/src/web/components/typeInputs/DateTypeInput.d.ts +1 -1
- package/dist/src/web/components/typeInputs/DateTypeInput.js +4 -0
- package/dist/src/web/components/typeInputs/EnumTypeInput.d.ts +1 -0
- package/dist/src/web/components/typeInputs/EnumTypeInput.js +2 -2
- package/dist/src/web/components/typeInputs/FloatTypeInput.d.ts +1 -1
- package/dist/src/web/components/typeInputs/FloatTypeInput.js +5 -1
- package/dist/src/web/components/typeInputs/IncludeIdentifierTypeInput.d.ts +1 -0
- package/dist/src/web/components/typeInputs/IncludeIdentifierTypeInput.js +2 -2
- package/dist/src/web/components/typeInputs/IntegerTypeInput.d.ts +1 -1
- package/dist/src/web/components/typeInputs/IntegerTypeInput.js +5 -1
- package/dist/src/web/components/typeInputs/NestedEntityMapTypeInput.d.ts +2 -1
- package/dist/src/web/components/typeInputs/NestedEntityMapTypeInput.js +21 -12
- package/dist/src/web/components/typeInputs/ObjectTypeInput.d.ts +2 -1
- package/dist/src/web/components/typeInputs/ObjectTypeInput.js +9 -4
- package/dist/src/web/components/typeInputs/ReferenceIdentifierTypeInput.d.ts +1 -1
- package/dist/src/web/components/typeInputs/ReferenceIdentifierTypeInput.js +5 -1
- package/dist/src/web/components/typeInputs/StringTypeInput.d.ts +1 -1
- package/dist/src/web/components/typeInputs/StringTypeInput.js +11 -3
- package/dist/src/web/components/typeInputs/TypeInput.d.ts +3 -2
- package/dist/src/web/components/typeInputs/TypeInput.js +16 -58
- package/dist/src/web/hooks/useInstanceNamesByEntity.d.ts +1 -1
- package/dist/src/web/hooks/useInstanceNamesByEntity.js +1 -1
- package/dist/src/web/hooks/useSecondaryDeclarations.d.ts +1 -1
- package/dist/src/web/hooks/useSecondaryDeclarations.js +3 -3
- package/dist/src/web/routes/CreateInstance.js +27 -13
- package/dist/src/web/routes/Entity.js +27 -13
- package/dist/src/web/routes/Home.js +15 -1
- package/dist/src/web/routes/Instance.js +27 -12
- package/dist/src/web/routes/NotFound.js +4 -0
- package/dist/src/web/utils/debug.d.ts +1 -0
- package/dist/src/web/utils/debug.js +5 -0
- package/package.json +6 -6
- package/public/css/styles.css +132 -38
- /package/dist/src/{node/renderers/Output.js → shared/config.js} +0 -0
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { useState } from "preact/hooks";
|
|
3
3
|
import { validateNumberConstraints } from "../../../shared/validation/number.js";
|
|
4
|
+
import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
|
|
4
5
|
import { ValidationErrors } from "./utils/ValidationErrors.js";
|
|
5
6
|
export const FloatTypeInput = ({ type, value, onChange }) => {
|
|
6
|
-
const [stringValue, setStringValue] = useState(value.toString());
|
|
7
|
+
const [stringValue, setStringValue] = useState(typeof value === "number" ? value.toString() : "");
|
|
8
|
+
if (typeof value !== "number") {
|
|
9
|
+
return _jsx(MismatchingTypeError, { expected: "float", actual: value });
|
|
10
|
+
}
|
|
7
11
|
const errors = validateNumberConstraints(type, value);
|
|
8
12
|
return (_jsxs("div", { class: "field", children: [_jsx("input", { type: "number", value: stringValue, onInput: event => {
|
|
9
13
|
setStringValue(event.currentTarget.value);
|
|
@@ -4,6 +4,7 @@ import type { InstanceNamesByEntity } from "../../hooks/useInstanceNamesByEntity
|
|
|
4
4
|
import type { GetDeclFromDeclName } from "../../hooks/useSecondaryDeclarations.ts";
|
|
5
5
|
type Props = {
|
|
6
6
|
type: SerializedIncludeIdentifierType;
|
|
7
|
+
path: string | undefined;
|
|
7
8
|
value: unknown;
|
|
8
9
|
instanceNamesByEntity: InstanceNamesByEntity;
|
|
9
10
|
getDeclFromDeclName: GetDeclFromDeclName;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { TypeInput } from "./TypeInput.js";
|
|
3
|
-
export const IncludeIdentifierTypeInput = ({ type, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
|
|
3
|
+
export const IncludeIdentifierTypeInput = ({ type, path, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
|
|
4
4
|
const decl = getDeclFromDeclName(type.reference);
|
|
5
5
|
if (decl === undefined) {
|
|
6
6
|
return (_jsxs("div", { role: "alert", children: ["Unresolved declaration identifier ", _jsx("code", { children: type.reference })] }));
|
|
7
7
|
}
|
|
8
|
-
return (_jsx(TypeInput, { type: decl.type, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
|
|
8
|
+
return (_jsx(TypeInput, { type: decl.type, path: path, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
|
|
9
9
|
};
|
|
@@ -2,7 +2,7 @@ import type { FunctionComponent } from "preact";
|
|
|
2
2
|
import type { SerializedIntegerType } from "../../../node/schema/types/primitives/IntegerType.ts";
|
|
3
3
|
type Props = {
|
|
4
4
|
type: SerializedIntegerType;
|
|
5
|
-
value:
|
|
5
|
+
value: unknown;
|
|
6
6
|
onChange: (value: number) => void;
|
|
7
7
|
};
|
|
8
8
|
export declare const IntegerTypeInput: FunctionComponent<Props>;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { useState } from "preact/hooks";
|
|
3
3
|
import { validateNumberConstraints } from "../../../shared/validation/number.js";
|
|
4
|
+
import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
|
|
4
5
|
import { ValidationErrors } from "./utils/ValidationErrors.js";
|
|
5
6
|
export const IntegerTypeInput = ({ type, value, onChange }) => {
|
|
6
|
-
const [stringValue, setStringValue] = useState(value.toString());
|
|
7
|
+
const [stringValue, setStringValue] = useState(typeof value === "number" ? value.toString() : "");
|
|
8
|
+
if (typeof value !== "number") {
|
|
9
|
+
return _jsx(MismatchingTypeError, { expected: "float", actual: value });
|
|
10
|
+
}
|
|
7
11
|
const errors = validateNumberConstraints(type, value);
|
|
8
12
|
return (_jsxs("div", { class: "field", children: [_jsx("input", { type: "number", value: stringValue, onInput: event => {
|
|
9
13
|
setStringValue(event.currentTarget.value);
|
|
@@ -4,7 +4,8 @@ import type { InstanceNamesByEntity } from "../../hooks/useInstanceNamesByEntity
|
|
|
4
4
|
import type { GetDeclFromDeclName } from "../../hooks/useSecondaryDeclarations.ts";
|
|
5
5
|
type Props = {
|
|
6
6
|
type: SerializedNestedEntityMapType;
|
|
7
|
-
|
|
7
|
+
path: string | undefined;
|
|
8
|
+
value: unknown;
|
|
8
9
|
instanceNamesByEntity: InstanceNamesByEntity;
|
|
9
10
|
getDeclFromDeclName: GetDeclFromDeclName;
|
|
10
11
|
onChange: (value: Record<string, unknown>) => void;
|
|
@@ -1,30 +1,39 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { useState } from "preact/hooks";
|
|
3
3
|
import { sortObjectKeysAlphabetically } from "../../../shared/utils/object.js";
|
|
4
|
+
import { toTitleCase } from "../../../shared/utils/string.js";
|
|
4
5
|
import { createTypeSkeleton } from "../../utils/typeSkeleton.js";
|
|
5
6
|
import { Select } from "../Select.js";
|
|
6
7
|
import { TypeInput } from "./TypeInput.js";
|
|
7
|
-
|
|
8
|
+
import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
|
|
9
|
+
export const NestedEntityMapTypeInput = ({ type, path, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
|
|
8
10
|
const [newKey, setNewKey] = useState("");
|
|
11
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
12
|
+
return _jsx(MismatchingTypeError, { expected: "object", actual: value });
|
|
13
|
+
}
|
|
9
14
|
const existingKeys = Object.keys(value);
|
|
10
15
|
const secondaryInstances = (instanceNamesByEntity[type.secondaryEntity] ?? [])
|
|
11
16
|
.slice()
|
|
12
17
|
.filter(instance => !existingKeys.includes(instance.id))
|
|
13
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
14
|
-
return (_jsxs("div", { class: "field field--container field--nestedentitymap", children: [existingKeys.length > 0 && (_jsx("ul", { children: Object.entries(value).map(([key, item]) =>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true }));
|
|
19
|
+
return (_jsxs("div", { class: "field field--container field--nestedentitymap", children: [existingKeys.length > 0 && (_jsx("ul", { children: Object.entries(value).map(([key, item]) => {
|
|
20
|
+
const name = instanceNamesByEntity[type.secondaryEntity]?.find(instance => instance.id === key)
|
|
21
|
+
?.name ?? key;
|
|
22
|
+
return (_jsxs("li", { class: "container-item dict-item", children: [_jsxs("div", { className: "container-item-header", children: [_jsx("div", { className: "container-item-title", children: _jsxs("span", { children: [_jsx("strong", { children: name }), " ", _jsx("span", { className: "id", children: key })] }) }), _jsx("div", { className: "btns", children: _jsxs("button", { class: "destructive", onClick: () => {
|
|
23
|
+
const newObj = { ...value };
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
25
|
+
delete newObj[key];
|
|
26
|
+
onChange(newObj);
|
|
27
|
+
}, children: ["Delete ", name] }) })] }), _jsx(TypeInput, { type: type.type, path: path === undefined ? key : `${path}.${key}`, value: item, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newItem => {
|
|
28
|
+
onChange(sortObjectKeysAlphabetically({ ...value, [key]: newItem }));
|
|
29
|
+
} })] }, key));
|
|
30
|
+
}) })), _jsxs("div", { class: "add-item-container", children: [_jsxs(Select, { value: newKey, onInput: event => {
|
|
22
31
|
setNewKey(event.currentTarget.value);
|
|
23
|
-
}, disabled: secondaryInstances.length === 0, children: [secondaryInstances.length === 0 ? (_jsx("option", { value: "", disabled: true, children: "No instances available" })) : (_jsx("option", { value: "", disabled: true, children: "No selected instance" })), secondaryInstances.map(instance => (_jsx("option", { value: instance.id, children: instance.name }, instance.id)))] }),
|
|
32
|
+
}, disabled: secondaryInstances.length === 0, children: [secondaryInstances.length === 0 ? (_jsx("option", { value: "", disabled: true, children: "No instances available" })) : (_jsx("option", { value: "", disabled: true, children: "No selected instance" })), secondaryInstances.map(instance => (_jsx("option", { value: instance.id, children: instance.name }, instance.id)))] }), _jsxs("button", { onClick: () => {
|
|
24
33
|
onChange(sortObjectKeysAlphabetically({
|
|
25
34
|
...value,
|
|
26
35
|
[newKey]: createTypeSkeleton(getDeclFromDeclName, type.type),
|
|
27
36
|
}));
|
|
28
37
|
setNewKey("");
|
|
29
|
-
}, disabled: newKey === "", children: "Add Key" })] })] }));
|
|
38
|
+
}, disabled: newKey === "", children: ["Add ", newKey === "" ? "Key" : toTitleCase(newKey)] })] })] }));
|
|
30
39
|
};
|
|
@@ -4,7 +4,8 @@ import type { InstanceNamesByEntity } from "../../hooks/useInstanceNamesByEntity
|
|
|
4
4
|
import type { GetDeclFromDeclName } from "../../hooks/useSecondaryDeclarations.ts";
|
|
5
5
|
type Props = {
|
|
6
6
|
type: SerializedObjectType;
|
|
7
|
-
|
|
7
|
+
path: string | undefined;
|
|
8
|
+
value: unknown;
|
|
8
9
|
instanceNamesByEntity: InstanceNamesByEntity;
|
|
9
10
|
getDeclFromDeclName: GetDeclFromDeclName;
|
|
10
11
|
onChange: (value: Record<string, unknown>) => void;
|
|
@@ -5,20 +5,25 @@ import { validateObjectConstraints } from "../../../shared/validation/object.js"
|
|
|
5
5
|
import { Markdown } from "../../utils/Markdown.js";
|
|
6
6
|
import { createTypeSkeleton } from "../../utils/typeSkeleton.js";
|
|
7
7
|
import { TypeInput } from "./TypeInput.js";
|
|
8
|
+
import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
|
|
8
9
|
import { ValidationErrors } from "./utils/ValidationErrors.js";
|
|
9
|
-
export const ObjectTypeInput = ({ type, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
|
|
10
|
+
export const ObjectTypeInput = ({ type, path, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
|
|
11
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
12
|
+
return _jsx(MismatchingTypeError, { expected: "object", actual: value });
|
|
13
|
+
}
|
|
10
14
|
const errors = validateObjectConstraints(type, Object.keys(type.properties), value);
|
|
11
|
-
return (_jsxs("div", { class: "field field--container field--object", children: [_jsx("ul", { children: Object.entries(type.properties).map(([key, memberDecl]) => (_jsxs("li", { class: "container-item object-item", children: [_jsxs("div", { className: "container-item-header", children: [_jsxs("div", { className: "container-item-title", children: [_jsx("strong", { children: toTitleCase(key) }), memberDecl.comment === undefined ? null : (_jsx(Markdown, { class: "comment", string: memberDecl.comment }))] }), memberDecl.isRequired ? null : value[key] ===
|
|
15
|
+
return (_jsxs("div", { class: "field field--container field--object", children: [_jsx("ul", { children: Object.entries(type.properties).map(([key, memberDecl]) => (_jsxs("li", { class: "container-item object-item", children: [_jsxs("div", { className: "container-item-header", children: [_jsxs("div", { className: "container-item-title", children: [_jsx("strong", { children: toTitleCase(key) }), memberDecl.comment === undefined ? null : (_jsx(Markdown, { class: "comment", string: memberDecl.comment }))] }), memberDecl.isRequired ? null : value[key] ===
|
|
16
|
+
undefined ? (_jsxs("button", { onClick: () => {
|
|
12
17
|
onChange(sortObjectKeys({
|
|
13
18
|
...value,
|
|
14
19
|
[key]: createTypeSkeleton(getDeclFromDeclName, memberDecl.type),
|
|
15
20
|
}, Object.keys(type.properties)));
|
|
16
|
-
}, children: "Add
|
|
21
|
+
}, children: ["Add ", toTitleCase(key)] })) : (_jsxs("button", { class: "destructive", onClick: () => {
|
|
17
22
|
const newObj = { ...value };
|
|
18
23
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
19
24
|
delete newObj[key];
|
|
20
25
|
onChange(newObj);
|
|
21
|
-
}, children: "Remove
|
|
26
|
+
}, children: ["Remove ", toTitleCase(key)] }))] }), memberDecl.isRequired || value[key] !== undefined ? (_jsx(TypeInput, { type: memberDecl.type, path: path === undefined ? key : `${path}.${key}`, value: value[key], instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: newItem => {
|
|
22
27
|
onChange(sortObjectKeys({ ...value, [key]: newItem }, Object.keys(type.properties)));
|
|
23
28
|
} })) : null] }, key))) }), _jsx(ValidationErrors, { errors: errors })] }));
|
|
24
29
|
};
|
|
@@ -3,7 +3,7 @@ import type { SerializedReferenceIdentifierType } from "../../../node/schema/typ
|
|
|
3
3
|
import type { InstanceNamesByEntity } from "../../hooks/useInstanceNamesByEntity.ts";
|
|
4
4
|
type Props = {
|
|
5
5
|
type: SerializedReferenceIdentifierType;
|
|
6
|
-
value:
|
|
6
|
+
value: unknown;
|
|
7
7
|
instanceNamesByEntity: InstanceNamesByEntity;
|
|
8
8
|
onChange: (value: string) => void;
|
|
9
9
|
};
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { Select } from "../Select.js";
|
|
3
|
+
import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
|
|
3
4
|
import { ValidationErrors } from "./utils/ValidationErrors.js";
|
|
4
5
|
export const ReferenceIdentifierTypeInput = ({ type, value, instanceNamesByEntity, onChange, }) => {
|
|
6
|
+
if (typeof value !== "string") {
|
|
7
|
+
return _jsx(MismatchingTypeError, { expected: "string identifier", actual: value });
|
|
8
|
+
}
|
|
5
9
|
const instances = (instanceNamesByEntity[type.entity] ?? [])
|
|
6
10
|
.slice()
|
|
7
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
11
|
+
.sort((a, b) => a.name.localeCompare(b.name, undefined, { numeric: true }));
|
|
8
12
|
return (_jsxs("div", { class: "field", children: [_jsxs(Select, { value: value, onInput: event => {
|
|
9
13
|
onChange(event.currentTarget.value);
|
|
10
14
|
}, disabled: instances.length === 0, "aria-invalid": !value, children: [instances.length === 0 ? (_jsx("option", { value: "", disabled: true, children: "No instances available" })) : (_jsx("option", { value: "", disabled: true, children: "No selected instance" })), instances.map(instance => (_jsx("option", { value: instance.id, children: instance.name }, instance.id)))] }), _jsx(ValidationErrors, { errors: !value ? [ReferenceError("no reference provided")] : [] })] }));
|
|
@@ -2,7 +2,7 @@ import type { FunctionComponent } from "preact";
|
|
|
2
2
|
import type { SerializedStringType } from "../../../node/schema/types/primitives/StringType.ts";
|
|
3
3
|
type Props = {
|
|
4
4
|
type: SerializedStringType;
|
|
5
|
-
value:
|
|
5
|
+
value: unknown;
|
|
6
6
|
onChange: (value: string) => void;
|
|
7
7
|
};
|
|
8
8
|
export declare const StringTypeInput: FunctionComponent<Props>;
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "preact/jsx-runtime";
|
|
2
2
|
import { validateStringConstraints } from "../../../shared/validation/string.js";
|
|
3
3
|
import { Markdown } from "../../utils/Markdown.js";
|
|
4
|
+
import { MismatchingTypeError } from "./utils/MismatchingTypeError.js";
|
|
4
5
|
import { ValidationErrors } from "./utils/ValidationErrors.js";
|
|
5
6
|
export const StringTypeInput = ({ type, value, onChange }) => {
|
|
7
|
+
if (typeof value !== "string") {
|
|
8
|
+
return _jsx(MismatchingTypeError, { expected: "string", actual: value });
|
|
9
|
+
}
|
|
6
10
|
const { minLength, maxLength, pattern, isMarkdown } = type;
|
|
7
11
|
const errors = validateStringConstraints(type, value);
|
|
8
|
-
return (_jsx("div", { class: "field field--string", children: isMarkdown ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "editor", children: [_jsx("textarea", { value: value, minLength: minLength, maxLength: maxLength, onInput: event => {
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
return (_jsx("div", { class: "field field--string", children: isMarkdown ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "editor", children: [_jsx("div", { class: "textarea-grow-wrap", "data-value": value, children: _jsx("textarea", { value: value, minLength: minLength, maxLength: maxLength, onInput: event => {
|
|
13
|
+
onChange(event.currentTarget.value);
|
|
14
|
+
}, "aria-invalid": errors.length > 0 }) }), _jsx(ValidationErrors, { errors: errors })] }), _jsx("div", { className: "preview", children: _jsx(Markdown, { string: value }) })] })) : (_jsxs("div", { className: "editor", children: [_jsx("input", { type: "text", value: value, minLength: minLength, maxLength: maxLength, pattern: pattern === undefined
|
|
15
|
+
? undefined
|
|
16
|
+
: pattern.startsWith("^(?:") && pattern.endsWith(")$")
|
|
17
|
+
? pattern.slice(4, -2)
|
|
18
|
+
: `.*${pattern}.*`, onInput: event => {
|
|
11
19
|
onChange(event.currentTarget.value);
|
|
12
20
|
}, "aria-invalid": errors.length > 0 }), _jsx(ValidationErrors, { errors: errors })] })) }));
|
|
13
21
|
};
|
|
@@ -4,10 +4,11 @@ import type { InstanceNamesByEntity } from "../../hooks/useInstanceNamesByEntity
|
|
|
4
4
|
import type { GetDeclFromDeclName } from "../../hooks/useSecondaryDeclarations.ts";
|
|
5
5
|
type Props = {
|
|
6
6
|
type: SerializedType;
|
|
7
|
+
path: string | undefined;
|
|
7
8
|
value: unknown;
|
|
8
9
|
instanceNamesByEntity: InstanceNamesByEntity;
|
|
9
10
|
getDeclFromDeclName: GetDeclFromDeclName;
|
|
10
11
|
onChange: (value: unknown) => void;
|
|
11
12
|
};
|
|
12
|
-
|
|
13
|
-
export {};
|
|
13
|
+
declare const MemoizedTypeInput: FunctionComponent<Props>;
|
|
14
|
+
export { MemoizedTypeInput as TypeInput };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "preact/jsx-runtime";
|
|
2
|
+
import { memo } from "preact/compat";
|
|
2
3
|
import { assertExhaustive } from "../../../shared/utils/typeSafety.js";
|
|
3
4
|
import { ArrayTypeInput } from "./ArrayTypeInput.js";
|
|
4
5
|
import { BooleanTypeInput } from "./BooleanTypeInput.js";
|
|
@@ -12,79 +13,36 @@ import { NestedEntityMapTypeInput } from "./NestedEntityMapTypeInput.js";
|
|
|
12
13
|
import { ObjectTypeInput } from "./ObjectTypeInput.js";
|
|
13
14
|
import { ReferenceIdentifierTypeInput } from "./ReferenceIdentifierTypeInput.js";
|
|
14
15
|
import { StringTypeInput } from "./StringTypeInput.js";
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
const TypeInput = ({ type, path, value, instanceNamesByEntity, getDeclFromDeclName, onChange, }) => {
|
|
17
|
+
// console.log("rendering node at path ", path ?? "<root>")
|
|
17
18
|
switch (type.kind) {
|
|
18
19
|
case "BooleanType":
|
|
19
|
-
|
|
20
|
-
return _jsx(BooleanTypeInput, { type: type, value: value, onChange: onChange });
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
return _jsx(MismatchingTypeError, { expected: "boolean", actual: value });
|
|
24
|
-
}
|
|
20
|
+
return _jsx(BooleanTypeInput, { type: type, value: value, onChange: onChange });
|
|
25
21
|
case "DateType":
|
|
26
|
-
|
|
27
|
-
return _jsx(DateTypeInput, { type: type, value: value, onChange: onChange });
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
return _jsx(MismatchingTypeError, { expected: "date string", actual: value });
|
|
31
|
-
}
|
|
22
|
+
return _jsx(DateTypeInput, { type: type, value: value, onChange: onChange });
|
|
32
23
|
case "FloatType":
|
|
33
|
-
|
|
34
|
-
return _jsx(FloatTypeInput, { type: type, value: value, onChange: onChange });
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
return _jsx(MismatchingTypeError, { expected: "float", actual: value });
|
|
38
|
-
}
|
|
24
|
+
return _jsx(FloatTypeInput, { type: type, value: value, onChange: onChange });
|
|
39
25
|
case "IntegerType":
|
|
40
|
-
|
|
41
|
-
return _jsx(IntegerTypeInput, { type: type, value: value, onChange: onChange });
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
return _jsx(MismatchingTypeError, { expected: "integer", actual: value });
|
|
45
|
-
}
|
|
26
|
+
return _jsx(IntegerTypeInput, { type: type, value: value, onChange: onChange });
|
|
46
27
|
case "StringType":
|
|
47
|
-
|
|
48
|
-
return _jsx(StringTypeInput, { type: type, value: value, onChange: onChange });
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
return _jsx(MismatchingTypeError, { expected: "string", actual: value });
|
|
52
|
-
}
|
|
28
|
+
return _jsx(StringTypeInput, { type: type, value: value, onChange: onChange });
|
|
53
29
|
case "ArrayType":
|
|
54
|
-
|
|
55
|
-
return (_jsx(ArrayTypeInput, { type: type, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
return _jsx(MismatchingTypeError, { expected: "array", actual: value });
|
|
59
|
-
}
|
|
30
|
+
return (_jsx(ArrayTypeInput, { type: type, path: path, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
|
|
60
31
|
case "ObjectType":
|
|
61
|
-
|
|
62
|
-
return (_jsx(ObjectTypeInput, { type: type, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
return _jsx(MismatchingTypeError, { expected: "object", actual: value });
|
|
66
|
-
}
|
|
32
|
+
return (_jsx(ObjectTypeInput, { type: type, path: path, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
|
|
67
33
|
case "TypeArgumentType":
|
|
68
34
|
return _jsx(TypeArgumentTypeInput, { type: type });
|
|
69
35
|
case "ReferenceIdentifierType":
|
|
70
|
-
|
|
71
|
-
return (_jsx(ReferenceIdentifierTypeInput, { type: type, value: value, instanceNamesByEntity: instanceNamesByEntity, onChange: onChange }));
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
return _jsx(MismatchingTypeError, { expected: "string identifier", actual: value });
|
|
75
|
-
}
|
|
36
|
+
return (_jsx(ReferenceIdentifierTypeInput, { type: type, value: value, instanceNamesByEntity: instanceNamesByEntity, onChange: onChange }));
|
|
76
37
|
case "IncludeIdentifierType":
|
|
77
|
-
return (_jsx(IncludeIdentifierTypeInput, { type: type, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
|
|
38
|
+
return (_jsx(IncludeIdentifierTypeInput, { type: type, path: path, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
|
|
78
39
|
case "NestedEntityMapType":
|
|
79
|
-
|
|
80
|
-
return (_jsx(NestedEntityMapTypeInput, { type: type, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
return _jsx(MismatchingTypeError, { expected: "entity map", actual: value });
|
|
84
|
-
}
|
|
40
|
+
return (_jsx(NestedEntityMapTypeInput, { type: type, path: path, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
|
|
85
41
|
case "EnumType":
|
|
86
|
-
return (_jsx(EnumTypeInput, { type: type, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
|
|
42
|
+
return (_jsx(EnumTypeInput, { type: type, path: path, value: value, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: onChange }));
|
|
87
43
|
default:
|
|
88
44
|
return assertExhaustive(type);
|
|
89
45
|
}
|
|
90
46
|
};
|
|
47
|
+
const MemoizedTypeInput = memo(TypeInput, (prevProps, nextProps) => prevProps.value === nextProps.value && prevProps.onChange === nextProps.onChange);
|
|
48
|
+
export { MemoizedTypeInput as TypeInput };
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { GetAllInstancesResponseBody } from "../../shared/api.ts";
|
|
2
2
|
export type InstanceNamesByEntity = GetAllInstancesResponseBody["instances"];
|
|
3
|
-
export declare const useInstanceNamesByEntity: (locales?: string[]) => [InstanceNamesByEntity, () => void];
|
|
3
|
+
export declare const useInstanceNamesByEntity: (locales?: string[]) => [InstanceNamesByEntity | undefined, () => void];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "preact/hooks";
|
|
2
2
|
import { getAllInstances } from "../api.js";
|
|
3
3
|
export const useInstanceNamesByEntity = (locales = []) => {
|
|
4
|
-
const [instanceNamesByEntity, setInstanceNamesByEntity] = useState(
|
|
4
|
+
const [instanceNamesByEntity, setInstanceNamesByEntity] = useState();
|
|
5
5
|
const updateInstanceNamesByEntity = useCallback(() => {
|
|
6
6
|
getAllInstances(locales)
|
|
7
7
|
.then(data => {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { SerializedSecondaryDecl } from "../../node/schema/declarations/Declaration.ts";
|
|
2
2
|
export type GetDeclFromDeclName = (name: string) => SerializedSecondaryDecl | undefined;
|
|
3
|
-
export declare const useGetDeclFromDeclName: () => GetDeclFromDeclName;
|
|
3
|
+
export declare const useGetDeclFromDeclName: () => [GetDeclFromDeclName, loaded: boolean];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "preact/hooks";
|
|
2
2
|
import { getAllDeclarations } from "../api.js";
|
|
3
3
|
export const useGetDeclFromDeclName = () => {
|
|
4
|
-
const [secondaryDeclarations, setSecondaryDeclarations] = useState(
|
|
4
|
+
const [secondaryDeclarations, setSecondaryDeclarations] = useState();
|
|
5
5
|
useEffect(() => {
|
|
6
6
|
getAllDeclarations()
|
|
7
7
|
.then(data => {
|
|
@@ -15,6 +15,6 @@ export const useGetDeclFromDeclName = () => {
|
|
|
15
15
|
}
|
|
16
16
|
});
|
|
17
17
|
}, []);
|
|
18
|
-
const getDeclFromDeclName = useCallback((name) => secondaryDeclarations
|
|
19
|
-
return getDeclFromDeclName;
|
|
18
|
+
const getDeclFromDeclName = useCallback((name) => secondaryDeclarations?.find(decl => decl.name === name), [secondaryDeclarations]);
|
|
19
|
+
return [getDeclFromDeclName, secondaryDeclarations !== undefined];
|
|
20
20
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { useLocation, useRoute } from "preact-iso";
|
|
3
3
|
import { useEffect, useState } from "preact/hooks";
|
|
4
|
-
import {
|
|
4
|
+
import { getSerializedDisplayNameFromEntityInstance } from "../../shared/utils/displayName.js";
|
|
5
5
|
import { toTitleCase } from "../../shared/utils/string.js";
|
|
6
6
|
import { validateLocaleIdentifier } from "../../shared/validation/identifier.js";
|
|
7
7
|
import { createInstanceByEntityNameAndId } from "../api.js";
|
|
@@ -15,7 +15,7 @@ import { createTypeSkeleton } from "../utils/typeSkeleton.js";
|
|
|
15
15
|
import { NotFound } from "./NotFound.js";
|
|
16
16
|
export const CreateInstance = () => {
|
|
17
17
|
const { params: { name }, } = useRoute();
|
|
18
|
-
const getDeclFromDeclName = useGetDeclFromDeclName();
|
|
18
|
+
const [getDeclFromDeclName, declsLoaded] = useGetDeclFromDeclName();
|
|
19
19
|
const entityFromRoute = useEntityFromRoute();
|
|
20
20
|
const [instanceNamesByEntity] = useInstanceNamesByEntity();
|
|
21
21
|
const [instance, setInstance] = useState();
|
|
@@ -26,10 +26,15 @@ export const CreateInstance = () => {
|
|
|
26
26
|
}
|
|
27
27
|
}, [getDeclFromDeclName, entityFromRoute]);
|
|
28
28
|
const { route } = useLocation();
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const entityName = entityFromRoute?.entity.name ?? name;
|
|
31
|
+
document.title =
|
|
32
|
+
entityName === undefined ? "Not found" : "New " + toTitleCase(entityName) + " — TSONDB";
|
|
33
|
+
}, [entityFromRoute?.entity.name, name]);
|
|
29
34
|
if (!name) {
|
|
30
35
|
return _jsx(NotFound, {});
|
|
31
36
|
}
|
|
32
|
-
if (!entityFromRoute) {
|
|
37
|
+
if (!entityFromRoute || !instanceNamesByEntity || !declsLoaded) {
|
|
33
38
|
return (_jsxs("div", { children: [_jsx("h1", { children: name }), _jsx("p", { className: "loading", children: "Loading \u2026" })] }));
|
|
34
39
|
}
|
|
35
40
|
const { entity, isLocaleEntity } = entityFromRoute;
|
|
@@ -39,13 +44,22 @@ export const CreateInstance = () => {
|
|
|
39
44
|
if (name) {
|
|
40
45
|
createInstanceByEntityNameAndId(entity.name, instance, isLocaleEntity ? customId : undefined)
|
|
41
46
|
.then(createdInstance => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
switch (name) {
|
|
48
|
+
case "saveandcontinue": {
|
|
49
|
+
route(`/entities/${entity.name}/instances/${createdInstance.instance.id}`);
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case "saveandaddanother": {
|
|
53
|
+
setInstance(createTypeSkeleton(getDeclFromDeclName, entity.type));
|
|
54
|
+
setCustomId("");
|
|
55
|
+
alert(`Instance of entity ${entity.name} created successfully with identifier ${createdInstance.instance.id}. You can add another instance now.`);
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case "save":
|
|
59
|
+
default: {
|
|
60
|
+
route(`/entities/${entity.name}?created=${encodeURIComponent(createdInstance.instance.id)}`);
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
49
63
|
}
|
|
50
64
|
})
|
|
51
65
|
.catch((error) => {
|
|
@@ -56,15 +70,15 @@ export const CreateInstance = () => {
|
|
|
56
70
|
}
|
|
57
71
|
};
|
|
58
72
|
const defaultName = customId || `New ${toTitleCase(entity.name)}`;
|
|
59
|
-
const instanceName =
|
|
73
|
+
const instanceName = getSerializedDisplayNameFromEntityInstance(entity, instance, defaultName);
|
|
60
74
|
const idErrors = isLocaleEntity ? validateLocaleIdentifier(customId) : [];
|
|
61
75
|
return (_jsxs(Layout, { breadcrumbs: [
|
|
62
76
|
{ url: "/", label: "Home" },
|
|
63
77
|
{ url: `/entities/${entity.name}`, label: entity.name },
|
|
64
78
|
], children: [_jsx("div", { class: "header-with-btns", children: _jsx("h1", { class: instanceName.length === 0 ? "empty-name" : undefined, children: instanceName || defaultName }) }), isLocaleEntity && (_jsxs("div", { class: "field field--id", children: [_jsx("label", { htmlFor: "id", children: "ID" }), _jsx("p", { className: "comment", children: "The instance\u2019s identifier. An IETF language tag (BCP47)." }), _jsx("input", { type: "text", id: "id", value: customId, required: true, pattern: "[a-z]{2,3}(-[A-Z]{2,3})?", placeholder: "en-US, de-DE, \u2026", onInput: event => {
|
|
65
79
|
setCustomId(event.currentTarget.value);
|
|
66
|
-
}, "aria-invalid": idErrors.length > 0 }), _jsx(ValidationErrors, { errors: idErrors })] })), _jsxs("form", { onSubmit: handleSubmit, children: [_jsx(TypeInput, { type: entity.type, value: instance, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: value => {
|
|
80
|
+
}, "aria-invalid": idErrors.length > 0 }), _jsx(ValidationErrors, { errors: idErrors })] })), _jsxs("form", { onSubmit: handleSubmit, children: [_jsx(TypeInput, { type: entity.type, path: undefined, value: instance, instanceNamesByEntity: instanceNamesByEntity, getDeclFromDeclName: getDeclFromDeclName, onChange: value => {
|
|
67
81
|
console.log("onChange", value);
|
|
68
82
|
setInstance(value);
|
|
69
|
-
} }), _jsxs("div", {
|
|
83
|
+
} }), _jsxs("div", { class: "form-footer btns", children: [_jsx("button", { type: "submit", class: "primary", name: "save", children: "Save" }), _jsx("button", { type: "submit", class: "primary", name: "saveandcontinue", children: "Save and Continue" }), _jsx("button", { type: "submit", class: "primary", name: "saveandaddanother", children: "Save and Add Another" })] })] })] }));
|
|
70
84
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
2
|
import { useRoute } from "preact-iso";
|
|
3
|
-
import { useEffect } from "preact/hooks";
|
|
3
|
+
import { useEffect, useState } from "preact/hooks";
|
|
4
4
|
import { getGitStatusForDisplay, getLabelForGitStatus } from "../../shared/utils/git.js";
|
|
5
|
+
import { toTitleCase } from "../../shared/utils/string.js";
|
|
5
6
|
import { deleteInstanceByEntityNameAndId, getEntityByName, getInstancesByEntityName, } from "../api.js";
|
|
6
7
|
import { Layout } from "../components/Layout.js";
|
|
7
8
|
import { useAPIResource } from "../hooks/useAPIResource.js";
|
|
@@ -11,8 +12,12 @@ import { NotFound } from "./NotFound.js";
|
|
|
11
12
|
const mapInstances = (data) => data.instances;
|
|
12
13
|
export const Entity = () => {
|
|
13
14
|
const { params: { name }, query: { created }, } = useRoute();
|
|
15
|
+
const [searchText, setSearchText] = useState("");
|
|
14
16
|
const [entity] = useAPIResource(getEntityByName, name ?? "");
|
|
15
17
|
const [instances, reloadInstances] = useMappedAPIResource(getInstancesByEntityName, mapInstances, name ?? "");
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
document.title = toTitleCase(entity?.declaration.namePlural ?? name ?? "") + " — TSONDB";
|
|
20
|
+
}, [entity?.declaration.namePlural, name]);
|
|
16
21
|
useEffect(() => {
|
|
17
22
|
if (created) {
|
|
18
23
|
const instanceElement = document.getElementById(`instance-${created}`);
|
|
@@ -25,20 +30,29 @@ export const Entity = () => {
|
|
|
25
30
|
return _jsx(NotFound, {});
|
|
26
31
|
}
|
|
27
32
|
if (!entity || !instances) {
|
|
28
|
-
return (_jsxs("div", { children: [_jsx("h1", { children: name }), _jsx("p", { className: "loading", children: "Loading \u2026" })] }));
|
|
33
|
+
return (_jsxs("div", { children: [_jsx("h1", { children: toTitleCase(entity?.declaration.namePlural ?? name) }), _jsx("p", { className: "loading", children: "Loading \u2026" })] }));
|
|
29
34
|
}
|
|
30
|
-
|
|
35
|
+
const lowerSearchText = searchText.toLowerCase();
|
|
36
|
+
const filteredInstances = searchText.length === 0
|
|
37
|
+
? instances
|
|
38
|
+
: instances.filter(instance => instance.id.includes(searchText) ||
|
|
39
|
+
instance.displayName.toLowerCase().includes(lowerSearchText));
|
|
40
|
+
return (_jsxs(Layout, { breadcrumbs: [{ url: "/", label: "Home" }], children: [_jsxs("div", { class: "header-with-btns", children: [_jsx("h1", { children: toTitleCase(entity.declaration.namePlural) }), _jsx("a", { class: "btn btn--primary", href: `/entities/${entity.declaration.name}/instances/create`, children: "Add" })] }), entity.declaration.comment && (_jsx(Markdown, { class: "description", string: entity.declaration.comment })), _jsxs("div", { className: "list-header", children: [_jsxs("p", { class: "instance-count", children: [searchText === "" ? "" : `${filteredInstances.length.toString()} of `, instances.length, " instance", instances.length === 1 ? "" : "s"] }), _jsxs("form", { action: "", rel: "search", onSubmit: e => {
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
}, children: [_jsx("label", { htmlFor: "instance-search", class: "visually-hidden", children: "Search" }), _jsx("input", { type: "text", id: "instance-search", value: searchText, onInput: event => {
|
|
43
|
+
setSearchText(event.currentTarget.value);
|
|
44
|
+
} })] })] }), _jsx("ul", { class: "entries entries--instances", children: filteredInstances.map(instance => {
|
|
31
45
|
const gitStatusForDisplay = getGitStatusForDisplay(instance.gitStatus);
|
|
32
|
-
return (_jsxs("li", { id: `instance-${instance.id}`, class: `
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
46
|
+
return (_jsxs("li", { id: `instance-${instance.id}`, class: `entries-item ${created === instance.id ? "entries-item--created" : ""} ${gitStatusForDisplay === undefined ? "" : `git-status--${gitStatusForDisplay}`}`, children: [_jsx("h2", { class: "entries-item__title", children: instance.displayName }), _jsx("p", { "aria-hidden": true, class: "entries-item__subtitle entries-item__subtitle--id", children: instance.id }), _jsxs("div", { class: "entries-item__side", children: [gitStatusForDisplay !== undefined && (_jsx("p", { class: `git-status git-status--${gitStatusForDisplay}`, title: getLabelForGitStatus(gitStatusForDisplay), children: gitStatusForDisplay })), _jsxs("div", { class: "btns", children: [_jsx("a", { href: `/entities/${entity.declaration.name}/instances/${instance.id}`, class: "btn", children: "Edit" }), _jsx("button", { class: "destructive", onClick: () => {
|
|
47
|
+
if (confirm("Are you sure you want to delete this instance?")) {
|
|
48
|
+
deleteInstanceByEntityNameAndId(entity.declaration.name, instance.id)
|
|
49
|
+
.then(() => reloadInstances())
|
|
50
|
+
.catch((error) => {
|
|
51
|
+
if (error instanceof Error) {
|
|
52
|
+
alert("Error deleting instance:\n\n" + error.toString());
|
|
53
|
+
}
|
|
54
|
+
});
|
|
39
55
|
}
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}, children: "Delete" })] })] }, instance.id));
|
|
56
|
+
}, children: "Delete" })] })] })] }, instance.id));
|
|
43
57
|
}) })] }));
|
|
44
58
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "preact/hooks";
|
|
2
3
|
import { toTitleCase } from "../../shared/utils/string.js";
|
|
3
4
|
import { getAllEntities } from "../api.js";
|
|
4
5
|
import { Layout } from "../components/Layout.js";
|
|
@@ -7,5 +8,18 @@ import { Markdown } from "../utils/Markdown.js";
|
|
|
7
8
|
const mapEntities = (data) => data.declarations.sort((a, b) => a.declaration.name.localeCompare(b.declaration.name));
|
|
8
9
|
export const Home = () => {
|
|
9
10
|
const [entities] = useMappedAPIResource(getAllEntities, mapEntities);
|
|
10
|
-
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
document.title = "Entities — TSONDB";
|
|
13
|
+
}, []);
|
|
14
|
+
const [searchText, setSearchText] = useState("");
|
|
15
|
+
const lowerSearchText = searchText.toLowerCase().replaceAll(" ", "");
|
|
16
|
+
const filteredEntities = searchText.length === 0
|
|
17
|
+
? entities
|
|
18
|
+
: entities?.filter(entity => entity.declaration.name.toLowerCase().includes(lowerSearchText) ||
|
|
19
|
+
entity.declaration.namePlural.toLowerCase().includes(lowerSearchText));
|
|
20
|
+
return (_jsxs(Layout, { breadcrumbs: [{ url: "/", label: "Home" }], children: [_jsx("h1", { children: "Entities" }), _jsxs("div", { className: "list-header", children: [_jsxs("p", { class: "instance-count", children: [searchText === "" ? "" : `${(filteredEntities?.length ?? 0).toString()} of `, entities?.length ?? 0, " entit", entities?.length === 1 ? "y" : "ies"] }), _jsxs("form", { action: "", rel: "search", onSubmit: e => {
|
|
21
|
+
e.preventDefault();
|
|
22
|
+
}, children: [_jsx("label", { htmlFor: "entity-search", class: "visually-hidden", children: "Search" }), _jsx("input", { type: "text", id: "entity-search", value: searchText, onInput: event => {
|
|
23
|
+
setSearchText(event.currentTarget.value);
|
|
24
|
+
} })] })] }), _jsx("ul", { class: "entries entries--entities", children: (filteredEntities ?? []).map(entity => (_jsxs("li", { class: "entries-item", children: [_jsxs("div", { class: "entries-item__title", children: [_jsx("h2", { children: toTitleCase(entity.declaration.namePlural) }), entity.declaration.comment && (_jsx(Markdown, { class: "description", string: entity.declaration.comment }))] }), _jsxs("p", { class: "entries-item__subtitle", children: [entity.instanceCount, " instance", entity.instanceCount === 1 ? "" : "s"] }), _jsx("div", { class: "entries-item__side", children: _jsx("div", { class: "btns", children: _jsx("a", { href: `/entities/${entity.declaration.name}`, class: "btn", children: "View" }) }) })] }, entity.declaration.name))) })] }));
|
|
11
25
|
};
|