Compare commits
No commits in common. "8dd55a4257512c24b7c8bfd7cc7fdd5676eee5ce" and "869a5309fb55c8b1f55babf8b6044cdcc10b1065" have entirely different histories.
8dd55a4257
...
869a5309fb
@ -4,8 +4,7 @@ import {
|
||||
indentNodeProp,
|
||||
delimitedIndent,
|
||||
foldNodeProp,
|
||||
foldInside,
|
||||
syntaxTree
|
||||
foldInside
|
||||
} from '@codemirror/language'
|
||||
import { styleTags, tags as t } from '@lezer/highlight'
|
||||
import { parseMixed } from '@lezer/common'
|
||||
@ -82,64 +81,13 @@ function buildJsScope(autoCompleteObject, templateType) {
|
||||
return typed
|
||||
}
|
||||
|
||||
function isResourceValueContext(state, position) {
|
||||
let node = syntaxTree(state).resolveInner(position, -1)
|
||||
let insideTemplateInterpolation = false
|
||||
|
||||
while (node) {
|
||||
if (node.name === 'Interpolation') {
|
||||
insideTemplateInterpolation = true
|
||||
}
|
||||
if (node.name === 'AttributeValue' || node.name === 'String') {
|
||||
return true
|
||||
}
|
||||
if (node.name === 'TemplateString') {
|
||||
return !insideTemplateInterpolation
|
||||
}
|
||||
node = node.parent
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function resourceCompletionSource(resourceNames) {
|
||||
const options = [...new Set(resourceNames)]
|
||||
.filter((name) => typeof name === 'string' && name.length > 0)
|
||||
.map((name) => ({
|
||||
label: `@[${name}]`,
|
||||
apply: `@[${name}]`,
|
||||
type: 'variable',
|
||||
detail: 'Template resource'
|
||||
}))
|
||||
|
||||
return (context) => {
|
||||
if (options.length === 0) return null
|
||||
|
||||
const before = context.state.sliceDoc(0, context.pos)
|
||||
const match = before.match(/@(?:\[[^\]\r\n]*)?$/)
|
||||
if (!match || !isResourceValueContext(context.state, context.pos)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
from: context.pos - match[0].length,
|
||||
options
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Language support for FarmControl EJS+XML document templates.
|
||||
* @param {{ autoCompleteObject?: object|string, templateType?: string, resourceNames?: string[] }} [options]
|
||||
* @param {{ autoCompleteObject?: object|string, templateType?: string }} [options]
|
||||
*/
|
||||
export function fcTemplateLang(options = {}) {
|
||||
const {
|
||||
autoCompleteObject,
|
||||
templateType = 'documentTemplate',
|
||||
resourceNames = []
|
||||
} = options
|
||||
const { autoCompleteObject, templateType = 'documentTemplate' } = options
|
||||
const jsScope = buildJsScope(autoCompleteObject, templateType)
|
||||
const completeResources = resourceCompletionSource(resourceNames)
|
||||
|
||||
return new LanguageSupport(fcTemplateLanguage, [
|
||||
xmlLanguage.data.of({
|
||||
@ -148,8 +96,6 @@ export function fcTemplateLang(options = {}) {
|
||||
fcTemplateAttributes
|
||||
)
|
||||
}),
|
||||
xmlLanguage.data.of({ autocomplete: completeResources }),
|
||||
javascriptLanguage.data.of({ autocomplete: completeResources }),
|
||||
autoCloseTags,
|
||||
...javascriptCompletionSupport(jsScope)
|
||||
])
|
||||
|
||||
@ -8,12 +8,10 @@ import useCollapseState from '../../hooks/useCollapseState.jsx'
|
||||
import NotesPanel from '../../common/NotesPanel.jsx'
|
||||
import InfoCollapse from '../../common/InfoCollapse.jsx'
|
||||
import ObjectInfo from '../../common/ObjectInfo.jsx'
|
||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||
import ViewButton from '../../common/ViewButton.jsx'
|
||||
import InfoCircleIcon from '../../../Icons/InfoCircleIcon.jsx'
|
||||
import NoteIcon from '../../../Icons/NoteIcon.jsx'
|
||||
import AuditLogIcon from '../../../Icons/AuditLogIcon.jsx'
|
||||
import FileIcon from '../../../Icons/FileIcon.jsx'
|
||||
import ObjectForm from '../../common/ObjectForm.jsx'
|
||||
import EditButtons from '../../common/EditButtons.jsx'
|
||||
import ObjectTableNavigationButtons from '../../common/ObjectTableNavigationButtons.jsx'
|
||||
@ -25,10 +23,7 @@ import ObjectTable from '../../common/ObjectTable.jsx'
|
||||
import InfoCollapsePlaceholder from '../../common/InfoCollapsePlaceholder.jsx'
|
||||
import InfoActionButtons from '../../common/InfoActionButtons.jsx'
|
||||
import UserNotifierToggle from '../../common/UserNotifierToggle.jsx'
|
||||
import {
|
||||
getModelByName,
|
||||
getModelProperty
|
||||
} from '../../../../database/ObjectModels.js'
|
||||
import { getModelByName } from '../../../../database/ObjectModels.js'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
|
||||
const log = loglevel.getLogger('DocumentTemplateInfo')
|
||||
@ -45,7 +40,6 @@ const DocumentTemplateInfo = () => {
|
||||
'DocumentTemplateInfo',
|
||||
{
|
||||
info: true,
|
||||
resources: true,
|
||||
stocks: true,
|
||||
notes: true,
|
||||
auditLogs: false
|
||||
@ -105,7 +99,7 @@ const DocumentTemplateInfo = () => {
|
||||
<Space size='small'>
|
||||
<ObjectActions
|
||||
type='documentTemplate'
|
||||
pageName='info'
|
||||
pageName='info'
|
||||
id={documentTemplateId}
|
||||
disabled={objectFormState.loading}
|
||||
objectData={objectFormState.objectData}
|
||||
@ -114,7 +108,6 @@ const DocumentTemplateInfo = () => {
|
||||
disabled={objectFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'DocumentTemplate Information' },
|
||||
{ key: 'resources', label: 'Resources' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
]}
|
||||
@ -168,27 +161,25 @@ const DocumentTemplateInfo = () => {
|
||||
loading={objectFormState.loading}
|
||||
ref={actionHandlerRef}
|
||||
>
|
||||
<ObjectForm
|
||||
id={documentTemplateId}
|
||||
type='documentTemplate'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
setCurrentObject={true}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
<InfoCollapse
|
||||
title='Document Template Information'
|
||||
icon={<InfoCircleIcon />}
|
||||
active={collapseState.info}
|
||||
onToggle={(expanded) => updateCollapseState('info', expanded)}
|
||||
collapseKey='info'
|
||||
>
|
||||
{({ loading, isEditing, objectData }) => (
|
||||
<Flex vertical gap={'large'}>
|
||||
<InfoCollapse
|
||||
title='Document Template Information'
|
||||
icon={<InfoCircleIcon />}
|
||||
active={collapseState.info}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('info', expanded)
|
||||
}
|
||||
collapseKey='info'
|
||||
>
|
||||
<ObjectForm
|
||||
id={documentTemplateId}
|
||||
type='documentTemplate'
|
||||
style={{ height: '100%' }}
|
||||
ref={objectFormRef}
|
||||
setCurrentObject={true}
|
||||
onStateChange={(state) => {
|
||||
setEditFormState((prev) => ({ ...prev, ...state }))
|
||||
}}
|
||||
>
|
||||
{({ loading, isEditing, objectData }) => {
|
||||
return (
|
||||
<ObjectInfo
|
||||
loading={loading}
|
||||
indicator={<LoadingOutlined />}
|
||||
@ -197,32 +188,14 @@ const DocumentTemplateInfo = () => {
|
||||
objectData={objectData}
|
||||
visibleProperties={{
|
||||
content: false,
|
||||
testObject: false,
|
||||
resources: false
|
||||
testObject: false
|
||||
}}
|
||||
labelWidth='210px'
|
||||
labelWidth='190px'
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Resources'
|
||||
icon={<FileIcon />}
|
||||
active={collapseState.resources}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('resources', expanded)
|
||||
}
|
||||
collapseKey='resources'
|
||||
>
|
||||
<ObjectProperty
|
||||
{...getModelProperty('documentTemplate', 'resources')}
|
||||
isEditing={isEditing}
|
||||
objectData={objectData}
|
||||
loading={loading}
|
||||
size='medium'
|
||||
/>
|
||||
</InfoCollapse>
|
||||
</Flex>
|
||||
)}
|
||||
</ObjectForm>
|
||||
)
|
||||
}}
|
||||
</ObjectForm>
|
||||
</InfoCollapse>
|
||||
</ActionHandler>
|
||||
|
||||
<InfoCollapse
|
||||
@ -237,10 +210,7 @@ const DocumentTemplateInfo = () => {
|
||||
indicator={<LoadingOutlined />}
|
||||
>
|
||||
<Card>
|
||||
<NotesPanel
|
||||
_id={documentTemplateId}
|
||||
type='documentTemplate'
|
||||
/>
|
||||
<NotesPanel _id={documentTemplateId} type='documentTemplate' />
|
||||
</Card>
|
||||
</Spin>
|
||||
</InfoCollapse>
|
||||
@ -260,10 +230,10 @@ const DocumentTemplateInfo = () => {
|
||||
<ObjectTable
|
||||
type='auditLog'
|
||||
masterFilter={{
|
||||
parent:
|
||||
getModelByName('documentTemplate').prefix +
|
||||
':' +
|
||||
documentTemplateId
|
||||
parent:
|
||||
getModelByName('documentTemplate').prefix +
|
||||
':' +
|
||||
documentTemplateId
|
||||
}}
|
||||
visibleColumns={{ _id: false, parent: false }}
|
||||
/>
|
||||
|
||||
@ -158,9 +158,7 @@ const EmailMessageInfo = () => {
|
||||
spinning={objectFormState.loading}
|
||||
indicator={<LoadingOutlined />}
|
||||
>
|
||||
<Card
|
||||
styles={{ body: { height: 'calc(100vh - 218px)' } }}
|
||||
>
|
||||
<Card>
|
||||
<TemplatePreview
|
||||
template={objectData?.template}
|
||||
content={objectData?.content}
|
||||
|
||||
@ -42,7 +42,6 @@ const EmailTemplateInfo = () => {
|
||||
'EmailTemplateInfo',
|
||||
{
|
||||
info: true,
|
||||
resources: true,
|
||||
attachments: true,
|
||||
notes: true,
|
||||
auditLogs: false
|
||||
@ -108,7 +107,6 @@ const EmailTemplateInfo = () => {
|
||||
disabled={objectFormState.loading}
|
||||
items={[
|
||||
{ key: 'info', label: 'Email Template Information' },
|
||||
{ key: 'resources', label: 'Resources' },
|
||||
{ key: 'attachments', label: 'Attachments' },
|
||||
{ key: 'notes', label: 'Notes' },
|
||||
{ key: 'auditLogs', label: 'Audit Logs' }
|
||||
@ -193,29 +191,11 @@ const EmailTemplateInfo = () => {
|
||||
visibleProperties={{
|
||||
content: false,
|
||||
testObject: false,
|
||||
resources: false,
|
||||
attachments: false
|
||||
}}
|
||||
labelWidth='210px'
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Resources'
|
||||
icon={<FileIcon />}
|
||||
active={collapseState.resources}
|
||||
onToggle={(expanded) =>
|
||||
updateCollapseState('resources', expanded)
|
||||
}
|
||||
collapseKey='resources'
|
||||
>
|
||||
<ObjectProperty
|
||||
{...getModelProperty('emailTemplate', 'resources')}
|
||||
isEditing={isEditing}
|
||||
objectData={objectData}
|
||||
loading={loading}
|
||||
size='medium'
|
||||
/>
|
||||
</InfoCollapse>
|
||||
<InfoCollapse
|
||||
title='Attachments'
|
||||
icon={<FileIcon />}
|
||||
|
||||
@ -30,10 +30,7 @@ import { ApiServerContext } from '../../context/ApiServerContext.jsx'
|
||||
import ScrollBox from '../../common/ScrollBox.jsx'
|
||||
import JsonObjectIcon from '../../../Icons/JsonObjectIcon.jsx'
|
||||
import ObjectProperty from '../../common/ObjectProperty.jsx'
|
||||
import {
|
||||
getModelProperty,
|
||||
getModelByName
|
||||
} from '../../../../database/ObjectModels.js'
|
||||
import { getModelProperty, getModelByName } from '../../../../database/ObjectModels.js'
|
||||
|
||||
const log = loglevel.getLogger('FileInfo')
|
||||
log.setLevel(config.logLevel)
|
||||
@ -90,7 +87,7 @@ const FileInfo = () => {
|
||||
download: () => {
|
||||
fetchFileContent(objectFormState?.objectData, true)
|
||||
return true
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return (
|
||||
@ -105,7 +102,7 @@ const FileInfo = () => {
|
||||
<Space size='small'>
|
||||
<ObjectActions
|
||||
type='file'
|
||||
pageName='info'
|
||||
pageName='info'
|
||||
id={fileId}
|
||||
disabled={objectFormState.loading}
|
||||
objectData={objectFormState.objectData}
|
||||
@ -206,7 +203,7 @@ const FileInfo = () => {
|
||||
collapseKey='preview'
|
||||
>
|
||||
{objectFormState?.objectData?._id ? (
|
||||
<Card styles={{ body: { minHeight: 'calc(100vh - 218px)' } }}>
|
||||
<Card>
|
||||
<FilePreview
|
||||
file={objectFormState?.objectData}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
@ -246,7 +243,7 @@ const FileInfo = () => {
|
||||
indicator={<LoadingOutlined />}
|
||||
>
|
||||
<Card>
|
||||
<NotesPanel _id={fileId} type='file' />
|
||||
<NotesPanel _id={fileId} type='file' />
|
||||
</Card>
|
||||
</Spin>
|
||||
</InfoCollapse>
|
||||
@ -265,7 +262,10 @@ const FileInfo = () => {
|
||||
<ObjectTable
|
||||
type='auditLog'
|
||||
masterFilter={{
|
||||
parent: getModelByName('file').prefix + ':' + fileId
|
||||
parent:
|
||||
getModelByName('file').prefix +
|
||||
':' +
|
||||
fileId
|
||||
}}
|
||||
visibleColumns={{ _id: false, parent: false }}
|
||||
/>
|
||||
|
||||
@ -166,7 +166,6 @@ export default function CodeBlockEditor({
|
||||
disabled = false,
|
||||
minimal = false,
|
||||
autoCompleteObject = null,
|
||||
resourceNames = [],
|
||||
errorLines = [],
|
||||
templateType,
|
||||
onCursorChange = null
|
||||
@ -182,14 +181,8 @@ export default function CodeBlockEditor({
|
||||
}, [onCursorChange])
|
||||
|
||||
const languageExtension = useMemo(
|
||||
() =>
|
||||
getCodeLanguageExtension(
|
||||
language,
|
||||
autoCompleteObject,
|
||||
templateType,
|
||||
resourceNames
|
||||
),
|
||||
[language, autoCompleteObject, templateType, resourceNames]
|
||||
() => getCodeLanguageExtension(language, autoCompleteObject, templateType),
|
||||
[language, autoCompleteObject, templateType]
|
||||
)
|
||||
|
||||
const errorLineExtension = useMemo(() => {
|
||||
@ -333,7 +326,6 @@ CodeBlockEditor.propTypes = {
|
||||
disabled: PropTypes.bool,
|
||||
minimal: PropTypes.bool,
|
||||
autoCompleteObject: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
||||
resourceNames: PropTypes.arrayOf(PropTypes.string),
|
||||
errorLines: PropTypes.arrayOf(PropTypes.number),
|
||||
templateType: PropTypes.string,
|
||||
onCursorChange: PropTypes.func
|
||||
|
||||
@ -11,14 +11,12 @@ import {
|
||||
import LoadingPlaceholder from './LoadingPlaceholder'
|
||||
import GCodePreview from './GCodePreview'
|
||||
import ThreeDPreview from './ThreeDPreview'
|
||||
import PDFPreview from './PDFPreview'
|
||||
import { AuthContext } from '../context/AuthContext'
|
||||
|
||||
const hasExplicitPreviewHeight = (height) =>
|
||||
typeof height === 'number' ||
|
||||
(typeof height === 'string' && height !== '100%' && height !== 'auto')
|
||||
|
||||
const FilePreview = ({ file, style = {} }) => {
|
||||
useEffect(() => {
|
||||
console.log('FILEPREVIEWFILE', file)
|
||||
}, [file])
|
||||
const { token } = useContext(AuthContext)
|
||||
const { fetchFileContent } = useContext(ApiServerContext)
|
||||
|
||||
@ -28,13 +26,6 @@ const FilePreview = ({ file, style = {} }) => {
|
||||
|
||||
const currentId = useRef(null)
|
||||
|
||||
const extension = (file?.extension || '').toLowerCase()
|
||||
const isGcode = ['.g', '.gcode'].includes(extension)
|
||||
const is3DModel = ['.stl', '.3mf'].includes(extension)
|
||||
const isImage = file?.type?.startsWith('image/') || false
|
||||
const isPdf =
|
||||
file?.type?.startsWith('application/pdf') || extension === '.pdf'
|
||||
|
||||
const fetchPreview = useCallback(async () => {
|
||||
if (error != null) {
|
||||
return
|
||||
@ -58,25 +49,6 @@ const FilePreview = ({ file, style = {} }) => {
|
||||
}
|
||||
}, [file._id, file?.type, fetchPreview, token])
|
||||
|
||||
if (isPdf) {
|
||||
if (error != null) {
|
||||
return <div style={{ color: 'red' }}>{error}</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<PDFPreview
|
||||
file={fileObjectUrl}
|
||||
loading={loading}
|
||||
style={{
|
||||
...style,
|
||||
height: hasExplicitPreviewHeight(style.height)
|
||||
? style.height
|
||||
: '72vh'
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (loading == true || !file?.type) {
|
||||
return <LoadingPlaceholder message={'Loading file preview...'} />
|
||||
}
|
||||
@ -85,6 +57,14 @@ const FilePreview = ({ file, style = {} }) => {
|
||||
return <div style={{ color: 'red' }}>{error}</div>
|
||||
}
|
||||
|
||||
const isGcode =
|
||||
['.g', '.gcode'].includes((file?.extension || '').toLowerCase()) || false
|
||||
|
||||
const is3DModel =
|
||||
['.stl', '.3mf'].includes((file?.extension || '').toLowerCase()) || false
|
||||
|
||||
const isImage = file?.type.startsWith('image/')
|
||||
|
||||
if (isGcode && fileObjectUrl) {
|
||||
return (
|
||||
<GCodePreview
|
||||
|
||||
@ -116,7 +116,6 @@ const ObjectProperty = ({
|
||||
height = 'auto',
|
||||
minimal = false,
|
||||
autoCompleteObject = null,
|
||||
resourceNames = [],
|
||||
errorLines = [],
|
||||
templateType,
|
||||
onCursorChange,
|
||||
@ -438,7 +437,6 @@ const ObjectProperty = ({
|
||||
readOnly={true}
|
||||
minimal={minimal}
|
||||
autoCompleteObject={autoCompleteObject}
|
||||
resourceNames={resourceNames}
|
||||
errorLines={errorLines}
|
||||
templateType={templateType}
|
||||
onCursorChange={onCursorChange}
|
||||
@ -992,7 +990,6 @@ const ObjectProperty = ({
|
||||
height={height}
|
||||
minimal={minimal}
|
||||
autoCompleteObject={autoCompleteObject}
|
||||
resourceNames={resourceNames}
|
||||
errorLines={errorLines}
|
||||
templateType={templateType}
|
||||
onCursorChange={onCursorChange}
|
||||
@ -1148,7 +1145,6 @@ ObjectProperty.propTypes = {
|
||||
name: PropTypes.string,
|
||||
language: PropTypes.string,
|
||||
autoCompleteObject: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
||||
resourceNames: PropTypes.arrayOf(PropTypes.string),
|
||||
errorLines: PropTypes.arrayOf(PropTypes.number),
|
||||
templateType: PropTypes.string,
|
||||
onCursorChange: PropTypes.func,
|
||||
|
||||
@ -35,9 +35,16 @@ const PDFPreview = ({ file, loading = false, style }) => {
|
||||
}}
|
||||
disabled={loading}
|
||||
/>
|
||||
<Button
|
||||
icon={<MinusIcon />}
|
||||
onClick={() => {
|
||||
setPreviewScale((prev) => clampPreviewScale(prev - 0.05))
|
||||
}}
|
||||
disabled={loading}
|
||||
/>
|
||||
<Button
|
||||
readOnly={true}
|
||||
style={{ minWidth: '70px' }}
|
||||
style={{ width: '65px' }}
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setPreviewScale(1)
|
||||
@ -45,13 +52,6 @@ const PDFPreview = ({ file, loading = false, style }) => {
|
||||
>
|
||||
{previewScale.toFixed(2)}x
|
||||
</Button>
|
||||
<Button
|
||||
icon={<MinusIcon />}
|
||||
onClick={() => {
|
||||
setPreviewScale((prev) => clampPreviewScale(prev - 0.05))
|
||||
}}
|
||||
disabled={loading}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<div
|
||||
|
||||
@ -94,16 +94,6 @@ const parseEjsErrorLines = (message) => {
|
||||
return [...lines]
|
||||
}
|
||||
|
||||
const normalizeResourceNames = (resources) => {
|
||||
if (!Array.isArray(resources)) return []
|
||||
|
||||
return resources
|
||||
.map((resource) =>
|
||||
typeof resource === 'string' ? resource : resource?.name
|
||||
)
|
||||
.filter((name) => typeof name === 'string' && name.length > 0)
|
||||
}
|
||||
|
||||
const TemplateEditor = ({
|
||||
objectData,
|
||||
template,
|
||||
@ -128,15 +118,11 @@ const TemplateEditor = ({
|
||||
const [errorLines, setErrorLines] = useState([])
|
||||
const [formatLoading, setFormatLoading] = useState(false)
|
||||
const [intellisense, setIntellisense] = useState(null)
|
||||
const [responseResources, setResponseResources] = useState(null)
|
||||
const intellisenseRequestIdRef = useRef(0)
|
||||
const intellisenseTimerRef = useRef(null)
|
||||
const resourceNames =
|
||||
responseResources ?? normalizeResourceNames(currentTemplate?.resources)
|
||||
|
||||
useEffect(() => {
|
||||
setIntellisense(null)
|
||||
setResponseResources(null)
|
||||
}, [currentTemplate?._id])
|
||||
|
||||
useEffect(() => {
|
||||
@ -182,9 +168,6 @@ const TemplateEditor = ({
|
||||
setIntellisense(result.intellisense)
|
||||
}
|
||||
}
|
||||
if (Array.isArray(result?.resources)) {
|
||||
setResponseResources(normalizeResourceNames(result.resources))
|
||||
}
|
||||
}
|
||||
)
|
||||
}, 200)
|
||||
@ -386,7 +369,6 @@ const TemplateEditor = ({
|
||||
autoCompleteObject={
|
||||
intellisense ?? currentTemplate?.testObject
|
||||
}
|
||||
resourceNames={resourceNames}
|
||||
errorLines={errorLines}
|
||||
templateType={templateType}
|
||||
onCursorChange={handleCursorChange}
|
||||
|
||||
@ -26,8 +26,7 @@ export function toEditorCode(code, language = 'javascript') {
|
||||
export function getCodeLanguageExtension(
|
||||
language = 'javascript',
|
||||
autoCompleteObject = null,
|
||||
templateType,
|
||||
resourceNames = []
|
||||
templateType
|
||||
) {
|
||||
const lang = Array.isArray(language) ? language[0] : language
|
||||
|
||||
@ -44,11 +43,7 @@ export function getCodeLanguageExtension(
|
||||
return xml()
|
||||
case 'fctemplatelang':
|
||||
case 'ejs':
|
||||
return fcTemplateLang({
|
||||
autoCompleteObject,
|
||||
templateType,
|
||||
resourceNames
|
||||
})
|
||||
return fcTemplateLang({ autoCompleteObject, templateType })
|
||||
case 'html':
|
||||
return html()
|
||||
case 'css':
|
||||
|
||||
@ -2240,14 +2240,12 @@ const ApiServerProvider = ({ children }) => {
|
||||
} catch (err) {
|
||||
const error = getTemplateErrorFromResponse(err)
|
||||
const intellisense = err?.response?.data?.intellisense
|
||||
const resources = err?.response?.data?.resources
|
||||
logger.error('Error fetching template intellisense:', error)
|
||||
const payload = {
|
||||
error,
|
||||
...(intellisense != null && typeof intellisense === 'object'
|
||||
? { intellisense }
|
||||
: {}),
|
||||
...(Array.isArray(resources) ? { resources } : {})
|
||||
: {})
|
||||
}
|
||||
if (typeof callback === 'function') {
|
||||
callback(payload)
|
||||
|
||||
@ -22,34 +22,6 @@ import ListIcon from '../../components/Icons/ListIcon'
|
||||
import DuplicateIcon from '../../components/Icons/DuplicateIcon'
|
||||
import BinIcon from '../../components/Icons/BinIcon'
|
||||
|
||||
const computeTemplateResources = (resources) => {
|
||||
if (!Array.isArray(resources)) return []
|
||||
|
||||
const usedNames = new Set()
|
||||
const nextSuffix = new Map()
|
||||
|
||||
return resources.map((resource) => {
|
||||
const filename = resource?.file?.filename || resource?.file?.name || ''
|
||||
const baseName =
|
||||
filename
|
||||
.replace(/\.[^.]*$/, '')
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '') || 'resource'
|
||||
let name = baseName
|
||||
let suffix = nextSuffix.get(baseName) || 1
|
||||
|
||||
while (usedNames.has(name)) {
|
||||
name = `${baseName}-${suffix}`
|
||||
suffix += 1
|
||||
}
|
||||
|
||||
usedNames.add(name)
|
||||
nextSuffix.set(baseName, suffix)
|
||||
return { ...resource, name }
|
||||
})
|
||||
}
|
||||
|
||||
export const DocumentTemplate = {
|
||||
name: 'documentTemplate',
|
||||
label: 'Document Template',
|
||||
@ -222,13 +194,6 @@ export const DocumentTemplate = {
|
||||
readOnly: true,
|
||||
columnWidth: 180
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'dateTime',
|
||||
readOnly: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
@ -237,7 +202,13 @@ export const DocumentTemplate = {
|
||||
columnWidth: 200,
|
||||
columnFixed: 'left'
|
||||
},
|
||||
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'dateTime',
|
||||
readOnly: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'objectType',
|
||||
label: 'Object Type',
|
||||
@ -317,38 +288,6 @@ export const DocumentTemplate = {
|
||||
type: 'codeBlock',
|
||||
language: 'fcTemplateLang'
|
||||
},
|
||||
{
|
||||
name: 'resources',
|
||||
label: 'Resources',
|
||||
type: 'objectChildren',
|
||||
required: false,
|
||||
canAddRemove: true,
|
||||
size: 'medium',
|
||||
columns: ['name', 'file'],
|
||||
value: (documentTemplate) =>
|
||||
computeTemplateResources(documentTemplate?.resources),
|
||||
properties: [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
readOnly: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'file',
|
||||
required: true,
|
||||
type: 'file',
|
||||
label: 'File',
|
||||
showPreview: false,
|
||||
showHyperlink: true,
|
||||
masterFilter: ['.pdf', '.jpeg', '.png', '.svg', '.gif'],
|
||||
thumbnail: true,
|
||||
columnWidth: 200
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'testObject',
|
||||
label: 'Test Object',
|
||||
|
||||
@ -242,7 +242,7 @@ export const EmailMessage = {
|
||||
},
|
||||
{
|
||||
name: 'recipientEmail',
|
||||
label: 'Recipient Email',
|
||||
label: 'Recipient',
|
||||
type: 'email',
|
||||
required: true,
|
||||
readOnly: readOnlyAfterCreate,
|
||||
@ -278,7 +278,7 @@ export const EmailMessage = {
|
||||
},
|
||||
{
|
||||
name: 'recipient',
|
||||
label: 'Recipient',
|
||||
label: 'Recipient Object',
|
||||
type: 'object',
|
||||
objectType: (data) => data?.recipientType,
|
||||
readOnly: readOnlyAfterCreate,
|
||||
|
||||
@ -22,34 +22,6 @@ import EmailTemplateIcon from '../../components/Icons/EmailTemplateIcon'
|
||||
import DuplicateIcon from '../../components/Icons/DuplicateIcon'
|
||||
import BinIcon from '../../components/Icons/BinIcon'
|
||||
|
||||
const computeTemplateResources = (resources) => {
|
||||
if (!Array.isArray(resources)) return []
|
||||
|
||||
const usedNames = new Set()
|
||||
const nextSuffix = new Map()
|
||||
|
||||
return resources.map((resource) => {
|
||||
const filename = resource?.file?.filename || resource?.file?.name || ''
|
||||
const baseName =
|
||||
filename
|
||||
.replace(/\.[^.]*$/, '')
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '') || 'resource'
|
||||
let name = baseName
|
||||
let suffix = nextSuffix.get(baseName) || 1
|
||||
|
||||
while (usedNames.has(name)) {
|
||||
name = `${baseName}-${suffix}`
|
||||
suffix += 1
|
||||
}
|
||||
|
||||
usedNames.add(name)
|
||||
nextSuffix.set(baseName, suffix)
|
||||
return { ...resource, name }
|
||||
})
|
||||
}
|
||||
|
||||
export const EmailTemplate = {
|
||||
name: 'emailTemplate',
|
||||
label: 'Email Template',
|
||||
@ -298,38 +270,6 @@ export const EmailTemplate = {
|
||||
objectType: (data) => data?.objectType,
|
||||
showHyperlink: true
|
||||
},
|
||||
{
|
||||
name: 'resources',
|
||||
label: 'Resources',
|
||||
type: 'objectChildren',
|
||||
required: false,
|
||||
canAddRemove: true,
|
||||
size: 'medium',
|
||||
columns: ['name', 'file'],
|
||||
value: (emailTemplate) =>
|
||||
computeTemplateResources(emailTemplate?.resources),
|
||||
properties: [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
readOnly: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'file',
|
||||
required: true,
|
||||
label: 'File',
|
||||
type: 'file',
|
||||
showPreview: false,
|
||||
showHyperlink: true,
|
||||
masterFilter: ['.pdf', '.jpeg', '.png', '.svg', '.gif'],
|
||||
thumbnail: true,
|
||||
columnWidth: 200
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'attachments',
|
||||
label: 'Attachments',
|
||||
|
||||
@ -156,13 +156,6 @@ export const File = {
|
||||
readOnly: true,
|
||||
columnWidth: 180
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'dateTime',
|
||||
readOnly: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
@ -171,7 +164,13 @@ export const File = {
|
||||
type: 'text',
|
||||
columnWidth: 200
|
||||
},
|
||||
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'dateTime',
|
||||
readOnly: true,
|
||||
columnWidth: 200
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
label: 'Type',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user