Skip to content

Commit 3c9fe8f

Browse files
Sine Jespersensinejespersen
authored andcommitted
5316: add poc how to make dynamic admin forms
1 parent 9900e5e commit 3c9fe8f

File tree

16 files changed

+697
-505
lines changed

16 files changed

+697
-505
lines changed

assets/admin/components/slide/content/content-form.jsx

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import FileSelector from "./file-selector";
88
import StationSelector from "./station/station-selector";
99
import RadioButtons from "../../util/forms/radio-buttons";
1010
import CheckboxOptions from "../../util/forms/checkbox-options";
11+
import getInputFiles from "../../../../shared/admin-util/helper";
1112

1213
/**
1314
* Render form elements for content form.
@@ -29,20 +30,6 @@ function ContentForm({
2930
onChange = null,
3031
mediaData = {},
3132
}) {
32-
const getInputFiles = (field) => {
33-
const inputFiles = [];
34-
35-
if (Array.isArray(field)) {
36-
field.forEach((mediaId) => {
37-
if (Object.prototype.hasOwnProperty.call(mediaData, mediaId)) {
38-
inputFiles.push(mediaData[mediaId]);
39-
}
40-
});
41-
}
42-
43-
return inputFiles;
44-
};
45-
4633
/**
4734
* @param {object} formData - The data for form input.
4835
* @returns {object | string} - Returns a rendered jsx object.
@@ -78,7 +65,7 @@ function ContentForm({
7865
)}
7966

8067
<FileSelector
81-
files={getInputFiles(formStateObject[formData.name])}
68+
files={getInputFiles(formStateObject[formData.name], mediaData)}
8269
multiple={formData.multipleImages}
8370
onFilesChange={onFileChange}
8471
name={formData.name}

assets/admin/components/slide/content/feed-selector.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ function FeedSelector({
168168
onChange={configurationChange}
169169
name={element.name}
170170
formStateObject={value?.configuration ?? {}}
171-
onFileChange={() => {}}
171+
onFileChange={() => {}} // Todo perhaps an error instead of an empty default
172172
/>
173173
);
174174
};

assets/admin/components/slide/content/file-dropzone.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function FileDropzone({ onFilesAdded, acceptedMimetypes = null }) {
4040
<>
4141
{/* TODO: Fix styling for dropzone: https://react-dropzone.js.org/#section-styling-dropzone */}
4242
{/* eslint-disable react/jsx-props-no-spreading */}
43-
<div {...getRootProps({ className: "dropzone drag-drop-area" })}>
43+
<div {...getRootProps({ className: "dropzone drag-drop-area my-1" })}>
4444
<input {...getInputProps()} />
4545
<div>
4646
<span>{t("file-dropzone.drag-and-drop-text")}</span>

assets/admin/components/slide/content/file-selector.jsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -107,19 +107,21 @@ function FileSelector({
107107
/>
108108
{enableMediaLibrary && (
109109
<>
110-
<Button
111-
disabled={!multiple && files.length > 0}
112-
variant="success"
113-
onClick={() => setShowMediaModal(true)}
114-
>
115-
{t("file-selector.open-media-library")}
116-
</Button>
117-
{/*
110+
<div className="d-flex align-items-center mt-2">
111+
<Button
112+
disabled={!multiple && files.length > 0}
113+
variant="success"
114+
onClick={() => setShowMediaModal(true)}
115+
>
116+
{t("file-selector.open-media-library")}
117+
</Button>
118+
{/*
118119
TODO: Make this configurable. It should always align with sizes in
119120
https://github.com/os2display/display-api-service/blob/develop/src/Entity/Tenant/Media.php
120121
*/}
121-
<div className="small mt-3">
122-
{t("file-selector.max-size")}: 200 MB
122+
<div className="small mt-1 mx-2">
123+
{t("file-selector.max-size")}: 200 MB
124+
</div>
123125
</div>
124126
<MediaSelectorModal
125127
selectedMedia={files}

assets/admin/components/slide/preview/slide-preview.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
22
import { Button } from "react-bootstrap";
33
import { useTranslation } from "react-i18next";
44
import ErrorBoundary from "../../error-boundary";
5-
import { renderSlide } from "../../../../shared/slide-utils/templates";
5+
import { renderSlide } from "../../../../shared/slide-utils/templates-slide";
66
import "./slide-preview.scss";
77

88
/**

assets/admin/components/slide/slide-form.jsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import userContext from "../../context/user-context";
2424
import Preview from "../preview/preview";
2525
import StickyFooter from "../util/sticky-footer";
2626
import Select from "../util/forms/select";
27-
import { getConfig } from "../../../shared/slide-utils/templates";
27+
import { renderAdminForm } from "../../../shared/slide-utils/templates-admin";
28+
import { getConfig } from "../../../shared/slide-utils/templates-slide";
2829
import "./slide-form.scss";
2930

3031
/**
@@ -267,6 +268,13 @@ function SlideForm({
267268
{selectedTemplate && contentFormElements && (
268269
<>
269270
<ContentBody>
271+
{renderAdminForm(
272+
idFromUrl(selectedTemplate.id),
273+
slide.content,
274+
handleContent,
275+
handleMedia,
276+
mediaData,
277+
)}
270278
{contentFormElements.map((formElement) => (
271279
<Fragment key={formElement.key}>
272280
{formElement.input === "feed" && (

assets/client/components/slide.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import ErrorBoundary from "./error-boundary.jsx";
22
import logger from "../logger/logger";
3-
import { renderSlide } from "../../shared/slide-utils/templates.js";
3+
import { renderSlide } from "../../shared/slide-utils/templates-slide.js";
44
import "./slide.scss";
55

66
/**

assets/shared/admin-util/helper.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const getInputFiles = (field, mediaData) => {
2+
const inputFiles = [];
3+
if (Array.isArray(field)) {
4+
field.forEach((mediaId) => {
5+
if (Object.prototype.hasOwnProperty.call(mediaData, mediaId)) {
6+
inputFiles.push(mediaData[mediaId]);
7+
}
8+
});
9+
}
10+
11+
return inputFiles;
12+
};
13+
14+
export default getInputFiles;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Load templates.
2+
// @see https://vite.dev/guide/features.html#glob-import
3+
// @see docs/custom-templates.md
4+
// Eager loading because no other code piece imports the templates otherwise.
5+
const templateModules = import.meta.glob("../templates/*.jsx", { eager: true });
6+
const customTemplatesModules = import.meta.glob("../custom-templates/*.jsx", {
7+
eager: true,
8+
});
9+
10+
function duckTypingAdminFormModule(module) {
11+
return (
12+
typeof module.id === "function" &&
13+
typeof module.config === "function" &&
14+
typeof module.renderAdminForm === "function"
15+
);
16+
}
17+
18+
function findAdminFormModule(modules, templateUlid) {
19+
for (const key of Object.keys(modules)) {
20+
const module = modules[key].default;
21+
22+
if (duckTypingAdminFormModule(module)) {
23+
if (module.id() === templateUlid) {
24+
return module;
25+
}
26+
}
27+
}
28+
29+
return null;
30+
}
31+
32+
function getAdminModule(templateUlid) {
33+
if (!templateUlid) {
34+
return null;
35+
}
36+
37+
const module =
38+
findAdminFormModule(templateModules, templateUlid) ??
39+
findAdminFormModule(customTemplatesModules, templateUlid) ??
40+
null;
41+
42+
if (module === null) {
43+
throw new Error(`Cannot find admin template with '${templateUlid}'`);
44+
}
45+
46+
return module;
47+
}
48+
49+
function renderAdminForm(
50+
templateUlid,
51+
formStateObject,
52+
onChange,
53+
handleMedia,
54+
mediaData,
55+
) {
56+
const module = getAdminModule(templateUlid);
57+
58+
if (!module) {
59+
return null;
60+
}
61+
62+
return module.renderAdminForm(
63+
formStateObject,
64+
onChange,
65+
handleMedia,
66+
mediaData,
67+
);
68+
}
69+
70+
export { renderAdminForm };
File renamed without changes.

0 commit comments

Comments
 (0)