@ynput/ayon-frontend-shared 0.2.5 → 0.2.7

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.
Files changed (83) hide show
  1. package/dist/ProjectTreeTable.cjs.js +16 -0
  2. package/dist/ProjectTreeTable.cjs.js.map +1 -1
  3. package/dist/ProjectTreeTable.es.js +16 -0
  4. package/dist/ProjectTreeTable.es.js.map +1 -1
  5. package/dist/_virtual/index.cjs5.js +5 -3
  6. package/dist/_virtual/index.cjs5.js.map +1 -1
  7. package/dist/_virtual/index.cjs6.js +3 -5
  8. package/dist/_virtual/index.cjs6.js.map +1 -1
  9. package/dist/_virtual/index.es5.js +5 -2
  10. package/dist/_virtual/index.es5.js.map +1 -1
  11. package/dist/_virtual/index.es6.js +2 -5
  12. package/dist/_virtual/index.es6.js.map +1 -1
  13. package/dist/node_modules/rehype-prism-plus/dist/index.es.cjs.js +1 -1
  14. package/dist/node_modules/rehype-prism-plus/dist/index.es.es.js +1 -1
  15. package/dist/node_modules/remove-accents/index.cjs.js +1 -1
  16. package/dist/node_modules/remove-accents/index.es.js +1 -1
  17. package/dist/shared/src/api/generated/actions.cjs.js.map +1 -1
  18. package/dist/shared/src/api/generated/actions.es.js.map +1 -1
  19. package/dist/shared/src/api/generated/graphql.cjs.js +2 -0
  20. package/dist/shared/src/api/generated/graphql.cjs.js.map +1 -1
  21. package/dist/shared/src/api/generated/graphql.es.js +2 -0
  22. package/dist/shared/src/api/generated/graphql.es.js.map +1 -1
  23. package/dist/shared/src/components/AttributeEditor/AttributeEditor.cjs.js +1 -1
  24. package/dist/shared/src/components/AttributeEditor/AttributeEditor.cjs.js.map +1 -1
  25. package/dist/shared/src/components/AttributeEditor/AttributeEditor.es.js +1 -1
  26. package/dist/shared/src/components/AttributeEditor/AttributeEditor.es.js.map +1 -1
  27. package/dist/shared/src/components/DetailsPanelAttributes/DetailsPanelAttributes.cjs.js +0 -1
  28. package/dist/shared/src/components/DetailsPanelAttributes/DetailsPanelAttributes.cjs.js.map +1 -1
  29. package/dist/shared/src/components/DetailsPanelAttributes/DetailsPanelAttributes.es.js +0 -1
  30. package/dist/shared/src/components/DetailsPanelAttributes/DetailsPanelAttributes.es.js.map +1 -1
  31. package/dist/shared/src/containers/Actions/Actions.cjs.js +5 -2
  32. package/dist/shared/src/containers/Actions/Actions.cjs.js.map +1 -1
  33. package/dist/shared/src/containers/Actions/Actions.es.js +5 -2
  34. package/dist/shared/src/containers/Actions/Actions.es.js.map +1 -1
  35. package/dist/shared/src/containers/Actions/ActionsDropdown/ActionsDropdown.styled.cjs.js +1 -0
  36. package/dist/shared/src/containers/Actions/ActionsDropdown/ActionsDropdown.styled.cjs.js.map +1 -1
  37. package/dist/shared/src/containers/Actions/ActionsDropdown/ActionsDropdown.styled.es.js +1 -0
  38. package/dist/shared/src/containers/Actions/ActionsDropdown/ActionsDropdown.styled.es.js.map +1 -1
  39. package/dist/shared/src/containers/DetailsPanel/DetailsPanelHeader/DetailsPanelHeader.cjs.js +9 -3
  40. package/dist/shared/src/containers/DetailsPanel/DetailsPanelHeader/DetailsPanelHeader.cjs.js.map +1 -1
  41. package/dist/shared/src/containers/DetailsPanel/DetailsPanelHeader/DetailsPanelHeader.es.js +9 -3
  42. package/dist/shared/src/containers/DetailsPanel/DetailsPanelHeader/DetailsPanelHeader.es.js.map +1 -1
  43. package/dist/shared/src/containers/Feed/context/FeedContext.cjs.js +2 -2
  44. package/dist/shared/src/containers/Feed/context/FeedContext.cjs.js.map +1 -1
  45. package/dist/shared/src/containers/Feed/context/FeedContext.es.js +2 -2
  46. package/dist/shared/src/containers/Feed/context/FeedContext.es.js.map +1 -1
  47. package/dist/shared/src/containers/ProjectTreeTable/ProjectTreeTable.cjs.js +4 -8
  48. package/dist/shared/src/containers/ProjectTreeTable/ProjectTreeTable.cjs.js.map +1 -1
  49. package/dist/shared/src/containers/ProjectTreeTable/ProjectTreeTable.es.js +4 -8
  50. package/dist/shared/src/containers/ProjectTreeTable/ProjectTreeTable.es.js.map +1 -1
  51. package/dist/shared/src/containers/ProjectTreeTable/buildTreeTableColumns.cjs.js +0 -1
  52. package/dist/shared/src/containers/ProjectTreeTable/buildTreeTableColumns.cjs.js.map +1 -1
  53. package/dist/shared/src/containers/ProjectTreeTable/buildTreeTableColumns.es.js +0 -1
  54. package/dist/shared/src/containers/ProjectTreeTable/buildTreeTableColumns.es.js.map +1 -1
  55. package/dist/shared/src/containers/ProjectTreeTable/utils/clientFilterToQueryFilter.cjs.js +1 -1
  56. package/dist/shared/src/containers/ProjectTreeTable/utils/clientFilterToQueryFilter.cjs.js.map +1 -1
  57. package/dist/shared/src/containers/ProjectTreeTable/utils/clientFilterToQueryFilter.es.js +1 -1
  58. package/dist/shared/src/containers/ProjectTreeTable/utils/clientFilterToQueryFilter.es.js.map +1 -1
  59. package/dist/shared/src/context/DetailsPanelContext.cjs.js +0 -1
  60. package/dist/shared/src/context/DetailsPanelContext.cjs.js.map +1 -1
  61. package/dist/shared/src/context/DetailsPanelContext.es.js +0 -1
  62. package/dist/shared/src/context/DetailsPanelContext.es.js.map +1 -1
  63. package/dist/shared/src/hooks/useActionTriggers.cjs.js +7 -6
  64. package/dist/shared/src/hooks/useActionTriggers.cjs.js.map +1 -1
  65. package/dist/shared/src/hooks/useActionTriggers.es.js +7 -6
  66. package/dist/shared/src/hooks/useActionTriggers.es.js.map +1 -1
  67. package/dist/shared/src/hooks/useLoadModules.cjs.js +9 -2
  68. package/dist/shared/src/hooks/useLoadModules.cjs.js.map +1 -1
  69. package/dist/shared/src/hooks/useLoadModules.es.js +9 -2
  70. package/dist/shared/src/hooks/useLoadModules.es.js.map +1 -1
  71. package/dist/shared/src/util/confirmDelete.cjs.js.map +1 -1
  72. package/dist/shared/src/util/confirmDelete.es.js.map +1 -1
  73. package/dist/types/SimpleTable/SimpleTable.d.ts +9 -2
  74. package/dist/types/SimpleTable/SimpleTableRowTemplate.d.ts +7 -0
  75. package/dist/types/api/generated/actions.d.ts +1 -1
  76. package/dist/types/api/generated/graphql.d.ts +3 -1
  77. package/dist/types/containers/Actions/Actions.d.ts +4 -3
  78. package/dist/types/containers/ProjectTreeTable/ProjectTreeTable.d.ts +7 -6
  79. package/dist/types/containers/ProjectTreeTable/index.d.ts +1 -0
  80. package/dist/types/context/DetailsPanelContext.d.ts +8 -2
  81. package/dist/types/hooks/useActionTriggers.d.ts +6 -1
  82. package/dist/types/util/confirmDelete.d.ts +1 -2
  83. package/package.json +1 -1
@@ -140,7 +140,7 @@ const AttributeEditor = ({
140
140
  if (formData) {
141
141
  if (isNew) {
142
142
  if (existingNames.includes(formData.name)) internalError = "This attribute already exists";
143
- else if (!formData.name.match("^[a-zA-Z_]{2,20}$")) internalError = "Invalid attribute name";
143
+ else if (!formData.name.match("^[a-zA-Z_]{2,64}$")) error = "Invalid attribute name";
144
144
  }
145
145
  }
146
146
  const handleSubmit = () => {
@@ -1 +1 @@
1
- {"version":3,"file":"AttributeEditor.cjs.js","sources":["../../../../../src/components/AttributeEditor/AttributeEditor.tsx"],"sourcesContent":["import { FC, useEffect, useState } from 'react'\nimport {\n SaveButton,\n Spacer,\n FormLayout,\n FormRow,\n InputText,\n InputSwitch,\n LockedInput,\n Dropdown,\n Dialog,\n Button,\n} from '@ynput/ayon-react-components'\nimport { camelCase, upperFirst } from 'lodash'\nimport { MinMaxField } from './components'\nimport { EnumEditor } from '@shared/components/EnumEditor'\nimport { AttributeData, AttributeModel, AttributeEnumItem } from '@shared/api'\n\nconst SCOPE_OPTIONS = [\n { value: 'project', label: 'Project' },\n { value: 'folder', label: 'Folder' },\n { value: 'task', label: 'Task' },\n { value: 'product', label: 'Product' },\n { value: 'version', label: 'Version' },\n { value: 'representation', label: 'Representation' },\n { value: 'user', label: 'User' },\n]\n\n// Define types for constants\ninterface GlobalFieldEntry {\n value: keyof AttributeData\n scope: (AttributeModel['scope'] | '')[] | null\n}\n\nconst GLOBAL_FIELDS: GlobalFieldEntry[] = [\n { value: 'description', scope: null },\n { value: 'example', scope: null },\n // @ts-expect-error - project is not a scope?\n { value: 'default', scope: ['project'] },\n { value: 'inherit', scope: null },\n]\n\ninterface TypeOptionDef {\n value: AttributeData['type']\n label: string\n fields: (keyof AttributeData)[]\n exclude?: (keyof AttributeData)[]\n}\n\ninterface TypeOptionsMap {\n [key: string]: TypeOptionDef\n}\n\nconst TYPE_OPTIONS: TypeOptionsMap = {\n string: {\n value: 'string',\n label: 'String',\n fields: ['minLength', 'maxLength', 'enum', 'regex'],\n },\n integer: {\n value: 'integer',\n label: 'Integer',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n float: {\n value: 'float',\n label: 'Decimal number',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n list_of_strings: {\n value: 'list_of_strings',\n label: 'List Of Strings',\n fields: ['minItems', 'maxItems', 'enum'],\n },\n boolean: {\n value: 'boolean',\n label: 'Boolean',\n fields: [],\n exclude: ['example'],\n },\n}\n\ntype Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>\ntype PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\nexport type AttributeForm = PartialBy<AttributeModel, 'scope' | 'position'>\ntype Excludes = (keyof Omit<AttributeModel, 'data'> | keyof AttributeData)[]\n\nconst initFormData: AttributeForm = {\n name: '',\n scope: ['folder', 'task'],\n builtin: false,\n position: 0,\n data: {\n type: 'string',\n title: '',\n description: '',\n example: '',\n default: undefined,\n enum: [],\n minLength: undefined,\n maxLength: undefined,\n regex: '',\n minItems: undefined,\n maxItems: undefined,\n ge: undefined,\n gt: undefined,\n le: undefined,\n lt: undefined,\n },\n}\n\n// build the form data based on excludes and to update any data\nconst buildInitFormData = (excludes: Excludes, data?: Partial<AttributeForm>) => {\n // Create a deep clone of init form data\n const formData = JSON.parse(JSON.stringify(initFormData)) as AttributeForm\n\n // Filter out top-level excludes if not in required\n const required = ['name']\n Object.keys(formData).forEach((key) => {\n if (\n !required.includes(key) &&\n excludes.includes(key as keyof Omit<AttributeModel, 'data'>) &&\n key !== 'data'\n ) {\n delete formData[key as keyof AttributeForm]\n }\n })\n\n // Filter out data field excludes if not in in required\n const requiredData = ['title']\n if (formData.data) {\n Object.keys(formData.data).forEach((key) => {\n if (!requiredData.includes(key) && excludes.includes(key as keyof AttributeData)) {\n delete formData.data[key as keyof AttributeData]\n }\n })\n }\n\n // Merge with provided data if any\n if (data) {\n // Merge top-level fields\n Object.keys(data).forEach((key) => {\n const typedKey = key as keyof AttributeForm\n if (typedKey !== 'data' && excludes.includes(typedKey)) return\n\n if (typedKey === 'data' && data.data && formData.data) {\n // Deep merge of data fields\n formData.data = { ...formData.data, ...data.data }\n } else if (data[typedKey] !== undefined) {\n // @ts-ignore - We know these properties exist\n formData[typedKey] = data[typedKey]\n }\n })\n }\n\n return formData\n}\n\nexport interface AttributeEditorProps {\n attribute: AttributeForm | null\n existingNames: string[]\n error?: string\n isUpdating?: boolean\n excludes?: Excludes\n onHide: () => void\n onEdit: (attribute: AttributeForm) => void\n onDelete?: () => void\n}\n\nexport const AttributeEditor: FC<AttributeEditorProps> = ({\n attribute,\n existingNames,\n error = '',\n isUpdating,\n excludes = [],\n onHide,\n onEdit,\n onDelete,\n}) => {\n const initForm = buildInitFormData(excludes, { position: existingNames.length })\n const [formData, setFormData] = useState<AttributeForm | null>(attribute || initForm)\n\n useEffect(() => {\n if (!!attribute) setFormData(attribute)\n }, [attribute])\n\n const isNew = !attribute\n\n // const setTopLevelData = (key: string, value: string) => {\n const setTopLevelData = <K extends keyof Omit<AttributeModel, 'data'>>(\n key: K,\n value: AttributeModel[K],\n ) => {\n setFormData((d) => {\n if (!d) {\n return d\n }\n return { ...d, [key]: value }\n })\n }\n\n // const setData = (key, value) => {\n const setData = <K extends keyof AttributeData>(key: K, value: AttributeData[K]) => {\n setFormData((d) => {\n // Add a check for d and d.data\n if (!d || !d.data) {\n return d\n }\n const dt = { ...d.data, [key]: value }\n return { ...d, data: dt }\n })\n }\n\n let internalError = ''\n if (formData) {\n if (isNew) {\n if (existingNames.includes(formData.name)) internalError = 'This attribute already exists'\n else if (!formData.name.match('^[a-zA-Z_]{2,20}$')) internalError = 'Invalid attribute name'\n } // name validation\n }\n\n const handleSubmit = () => {\n if (formData) {\n onEdit(formData)\n }\n }\n\n const footer = (\n <div style={{ display: 'flex', width: '100%', flexDirection: 'row' }}>\n {onDelete && attribute && (\n <Button\n variant=\"danger\"\n label={'Delete attribute'}\n icon={'delete'}\n disabled={isUpdating}\n onClick={onDelete}\n />\n )}\n <Spacer />\n <SaveButton\n label={isNew ? 'Create Attribute' : 'Save Attribute'}\n icon={'check'}\n disabled={!!internalError || !formData}\n active={!internalError && !!formData}\n saving={isUpdating}\n onClick={handleSubmit}\n />\n </div>\n )\n\n let dataFields: (keyof AttributeData)[] = []\n\n // add global fields, only if scope are null (all) or the scope is included\n GLOBAL_FIELDS.forEach((globalField) => {\n // @ts-expect-error - project scope will never be found here?\n if (!globalField?.scope || globalField?.scope?.some((s) => formData?.scope?.includes(s))) {\n dataFields.push(globalField.value)\n }\n })\n\n if (formData?.data.type && TYPE_OPTIONS[formData.data.type]) {\n const typeOpt = TYPE_OPTIONS[formData.data.type]\n dataFields = [...dataFields, ...typeOpt.fields].filter((f) => !typeOpt.exclude?.includes(f))\n }\n\n type CustomFieldRenderer = (value: any, onChange: (newValue: any) => void) => JSX.Element | null\n const customFields: {\n enum: CustomFieldRenderer\n inherit: CustomFieldRenderer\n booleanDefault: CustomFieldRenderer\n } = {\n enum: (value = [], onChange) => (\n <EnumEditor\n values={value as AttributeEnumItem[]}\n onChange={(val) => {\n onChange(val)\n }}\n />\n ),\n inherit: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n booleanDefault: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n }\n\n const handleTitleChange = (e: React.ChangeEvent) => {\n const v = (e.target as HTMLInputElement).value\n setData('title', v)\n\n if (isNew) {\n setTopLevelData('name', camelCase(v))\n }\n }\n\n return (\n <Dialog\n header={formData?.data?.title || formData?.name || 'New attribute'}\n footer={footer}\n onClose={onHide}\n isOpen={true}\n style={{ width: 700, zIndex: 999 }}\n size=\"full\"\n onKeyDown={(e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault()\n handleSubmit()\n }\n }}\n >\n {formData && (\n <FormLayout>\n {!excludes.includes('title') && (\n <FormRow label={'Title'} key={'title'}>\n <InputText value={formData?.data['title']} onChange={handleTitleChange} autoFocus />\n </FormRow>\n )}\n {!excludes.includes('name') && (\n <FormRow label={'Name'} key={'name'}>\n <LockedInput\n value={formData.name}\n disabled={!isNew}\n onSubmit={(v) => setTopLevelData('name', v)}\n label=\"name\"\n />\n </FormRow>\n )}\n {!excludes.includes('scope') && (\n <FormRow label=\"Scope\">\n <Dropdown\n options={SCOPE_OPTIONS}\n disabled={formData.builtin}\n value={formData.scope || []}\n onChange={(v) => setTopLevelData('scope', v as AttributeModel['scope'])}\n multiSelect\n widthExpand\n />\n </FormRow>\n )}\n {!excludes.includes('type') && (\n <FormRow label=\"Type\">\n <Dropdown\n value={[formData?.data?.type]}\n disabled={formData.builtin}\n options={Object.values(TYPE_OPTIONS)}\n onChange={(v) => setData('type', v[0] as AttributeData['type'])}\n minSelected={1}\n widthExpand\n />\n </FormRow>\n )}\n {dataFields.map((field) => {\n // skip if field is excluded\n if (excludes.includes(field)) return null\n\n let fieldComp = null\n let fieldLabel = upperFirst(field)\n\n if (field === 'enum' || field === 'inherit') {\n const renderer = customFields[field as 'enum' | 'inherit']\n fieldComp = renderer(formData?.data[field], (value) => setData(field, value))\n } else if (field === 'default' && formData?.data?.type === 'boolean') {\n fieldComp = customFields['booleanDefault'](\n formData?.data[field] as boolean,\n (value) => setData(field, value as AttributeData['default']),\n )\n } else if (['ge', 'gt', 'le', 'lt'].includes(field)) {\n // ignore gt and lt\n if (['gt', 'lt'].includes(field)) return null\n fieldComp = (\n <MinMaxField\n value={formData?.data}\n isMin={field === 'ge'}\n isFloat={formData?.data?.type === 'float'}\n onChange={(v) => {\n const geValue = v.ge !== undefined ? Number(v.ge) : undefined\n const leValue = v.le !== undefined ? Number(v.le) : undefined\n\n if (\n // @ts-expect-error\n (v.ge !== undefined && isNaN(geValue)) ||\n // @ts-expect-error\n (v.le !== undefined && isNaN(leValue))\n ) {\n // Do not update the form if the value is not a valid number\n return\n }\n\n setFormData((d) => {\n if (!d || !d.data) return d\n const dt = { ...d.data, ...v }\n return { ...d, data: dt }\n })\n }}\n />\n )\n\n // rewrite field to min or max for display label\n fieldLabel = field === 'ge' ? 'Min' : 'Max'\n } else {\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const strValue = e.target.value\n switch (field) {\n case 'minLength':\n case 'maxLength':\n case 'minItems':\n case 'maxItems': {\n const num = parseInt(strValue, 10)\n setData(field, isNaN(num) ? undefined : num)\n break\n }\n default:\n // For string fields ('description', 'regex') or 'any' type fields ('example', 'default')\n setData(field, strValue as AttributeData[typeof field])\n break\n }\n }\n\n fieldComp = (\n <InputText\n value={String(formData?.data[field] ?? '')}\n onChange={handleInputChange}\n />\n )\n }\n\n return (\n <FormRow\n label={fieldLabel}\n key={field}\n style={{\n alignItems: 'flex-start',\n }}\n >\n {fieldComp}\n </FormRow>\n )\n })}\n <span>\n {(internalError || error) && (\n <span className=\"form-error-text\">{internalError || error}</span>\n )}\n </span>\n </FormLayout>\n )}\n </Dialog>\n )\n}\n"],"names":["useState","useEffect","jsxs","jsx","Button","Spacer","SaveButton","_a","EnumEditor","InputSwitch","camelCase","Dialog","FormLayout","FormRow","InputText","LockedInput","Dropdown","upperFirst","MinMaxField","_b"],"mappings":"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"}
1
+ {"version":3,"file":"AttributeEditor.cjs.js","sources":["../../../../../src/components/AttributeEditor/AttributeEditor.tsx"],"sourcesContent":["import { FC, useEffect, useState } from 'react'\nimport {\n SaveButton,\n Spacer,\n FormLayout,\n FormRow,\n InputText,\n InputSwitch,\n LockedInput,\n Dropdown,\n Dialog,\n Button,\n} from '@ynput/ayon-react-components'\nimport { camelCase, upperFirst } from 'lodash'\nimport { MinMaxField } from './components'\nimport { EnumEditor } from '@shared/components/EnumEditor'\nimport { AttributeData, AttributeModel, AttributeEnumItem } from '@shared/api'\n\nconst SCOPE_OPTIONS = [\n { value: 'project', label: 'Project' },\n { value: 'folder', label: 'Folder' },\n { value: 'task', label: 'Task' },\n { value: 'product', label: 'Product' },\n { value: 'version', label: 'Version' },\n { value: 'representation', label: 'Representation' },\n { value: 'user', label: 'User' },\n]\n\n// Define types for constants\ninterface GlobalFieldEntry {\n value: keyof AttributeData\n scope: (AttributeModel['scope'] | '')[] | null\n}\n\nconst GLOBAL_FIELDS: GlobalFieldEntry[] = [\n { value: 'description', scope: null },\n { value: 'example', scope: null },\n // @ts-expect-error - project is not a scope?\n { value: 'default', scope: ['project'] },\n { value: 'inherit', scope: null },\n]\n\ninterface TypeOptionDef {\n value: AttributeData['type']\n label: string\n fields: (keyof AttributeData)[]\n exclude?: (keyof AttributeData)[]\n}\n\ninterface TypeOptionsMap {\n [key: string]: TypeOptionDef\n}\n\nconst TYPE_OPTIONS: TypeOptionsMap = {\n string: {\n value: 'string',\n label: 'String',\n fields: ['minLength', 'maxLength', 'enum', 'regex'],\n },\n integer: {\n value: 'integer',\n label: 'Integer',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n float: {\n value: 'float',\n label: 'Decimal number',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n list_of_strings: {\n value: 'list_of_strings',\n label: 'List Of Strings',\n fields: ['minItems', 'maxItems', 'enum'],\n },\n boolean: {\n value: 'boolean',\n label: 'Boolean',\n fields: [],\n exclude: ['example'],\n },\n}\n\ntype Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>\ntype PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\nexport type AttributeForm = PartialBy<AttributeModel, 'scope' | 'position'>\ntype Excludes = (keyof Omit<AttributeModel, 'data'> | keyof AttributeData)[]\n\nconst initFormData: AttributeForm = {\n name: '',\n scope: ['folder', 'task'],\n builtin: false,\n position: 0,\n data: {\n type: 'string',\n title: '',\n description: '',\n example: '',\n default: undefined,\n enum: [],\n minLength: undefined,\n maxLength: undefined,\n regex: '',\n minItems: undefined,\n maxItems: undefined,\n ge: undefined,\n gt: undefined,\n le: undefined,\n lt: undefined,\n },\n}\n\n// build the form data based on excludes and to update any data\nconst buildInitFormData = (excludes: Excludes, data?: Partial<AttributeForm>) => {\n // Create a deep clone of init form data\n const formData = JSON.parse(JSON.stringify(initFormData)) as AttributeForm\n\n // Filter out top-level excludes if not in required\n const required = ['name']\n Object.keys(formData).forEach((key) => {\n if (\n !required.includes(key) &&\n excludes.includes(key as keyof Omit<AttributeModel, 'data'>) &&\n key !== 'data'\n ) {\n delete formData[key as keyof AttributeForm]\n }\n })\n\n // Filter out data field excludes if not in in required\n const requiredData = ['title']\n if (formData.data) {\n Object.keys(formData.data).forEach((key) => {\n if (!requiredData.includes(key) && excludes.includes(key as keyof AttributeData)) {\n delete formData.data[key as keyof AttributeData]\n }\n })\n }\n\n // Merge with provided data if any\n if (data) {\n // Merge top-level fields\n Object.keys(data).forEach((key) => {\n const typedKey = key as keyof AttributeForm\n if (typedKey !== 'data' && excludes.includes(typedKey)) return\n\n if (typedKey === 'data' && data.data && formData.data) {\n // Deep merge of data fields\n formData.data = { ...formData.data, ...data.data }\n } else if (data[typedKey] !== undefined) {\n // @ts-ignore - We know these properties exist\n formData[typedKey] = data[typedKey]\n }\n })\n }\n\n return formData\n}\n\nexport interface AttributeEditorProps {\n attribute: AttributeForm | null\n existingNames: string[]\n error?: string\n isUpdating?: boolean\n excludes?: Excludes\n onHide: () => void\n onEdit: (attribute: AttributeForm) => void\n onDelete?: () => void\n}\n\nexport const AttributeEditor: FC<AttributeEditorProps> = ({\n attribute,\n existingNames,\n error = '',\n isUpdating,\n excludes = [],\n onHide,\n onEdit,\n onDelete,\n}) => {\n const initForm = buildInitFormData(excludes, { position: existingNames.length })\n const [formData, setFormData] = useState<AttributeForm | null>(attribute || initForm)\n\n useEffect(() => {\n if (!!attribute) setFormData(attribute)\n }, [attribute])\n\n const isNew = !attribute\n\n // const setTopLevelData = (key: string, value: string) => {\n const setTopLevelData = <K extends keyof Omit<AttributeModel, 'data'>>(\n key: K,\n value: AttributeModel[K],\n ) => {\n setFormData((d) => {\n if (!d) {\n return d\n }\n return { ...d, [key]: value }\n })\n }\n\n // const setData = (key, value) => {\n const setData = <K extends keyof AttributeData>(key: K, value: AttributeData[K]) => {\n setFormData((d) => {\n // Add a check for d and d.data\n if (!d || !d.data) {\n return d\n }\n const dt = { ...d.data, [key]: value }\n return { ...d, data: dt }\n })\n }\n\n let internalError = ''\n if (formData) {\n if (isNew) {\n if (existingNames.includes(formData.name)) internalError = 'This attribute already exists'\n else if (!formData.name.match('^[a-zA-Z_]{2,64}$')) error = 'Invalid attribute name'\n } // name validation\n }\n\n const handleSubmit = () => {\n if (formData) {\n onEdit(formData)\n }\n }\n\n const footer = (\n <div style={{ display: 'flex', width: '100%', flexDirection: 'row' }}>\n {onDelete && attribute && (\n <Button\n variant=\"danger\"\n label={'Delete attribute'}\n icon={'delete'}\n disabled={isUpdating}\n onClick={onDelete}\n />\n )}\n <Spacer />\n <SaveButton\n label={isNew ? 'Create Attribute' : 'Save Attribute'}\n icon={'check'}\n disabled={!!internalError || !formData}\n active={!internalError && !!formData}\n saving={isUpdating}\n onClick={handleSubmit}\n />\n </div>\n )\n\n let dataFields: (keyof AttributeData)[] = []\n\n // add global fields, only if scope are null (all) or the scope is included\n GLOBAL_FIELDS.forEach((globalField) => {\n // @ts-expect-error - project scope will never be found here?\n if (!globalField?.scope || globalField?.scope?.some((s) => formData?.scope?.includes(s))) {\n dataFields.push(globalField.value)\n }\n })\n\n if (formData?.data.type && TYPE_OPTIONS[formData.data.type]) {\n const typeOpt = TYPE_OPTIONS[formData.data.type]\n dataFields = [...dataFields, ...typeOpt.fields].filter((f) => !typeOpt.exclude?.includes(f))\n }\n\n type CustomFieldRenderer = (value: any, onChange: (newValue: any) => void) => JSX.Element | null\n const customFields: {\n enum: CustomFieldRenderer\n inherit: CustomFieldRenderer\n booleanDefault: CustomFieldRenderer\n } = {\n enum: (value = [], onChange) => (\n <EnumEditor\n values={value as AttributeEnumItem[]}\n onChange={(val) => {\n onChange(val)\n }}\n />\n ),\n inherit: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n booleanDefault: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n }\n\n const handleTitleChange = (e: React.ChangeEvent) => {\n const v = (e.target as HTMLInputElement).value\n setData('title', v)\n\n if (isNew) {\n setTopLevelData('name', camelCase(v))\n }\n }\n\n return (\n <Dialog\n header={formData?.data?.title || formData?.name || 'New attribute'}\n footer={footer}\n onClose={onHide}\n isOpen={true}\n style={{ width: 700, zIndex: 999 }}\n size=\"full\"\n onKeyDown={(e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault()\n handleSubmit()\n }\n }}\n >\n {formData && (\n <FormLayout>\n {!excludes.includes('title') && (\n <FormRow label={'Title'} key={'title'}>\n <InputText value={formData?.data['title']} onChange={handleTitleChange} autoFocus />\n </FormRow>\n )}\n {!excludes.includes('name') && (\n <FormRow label={'Name'} key={'name'}>\n <LockedInput\n value={formData.name}\n disabled={!isNew}\n onSubmit={(v) => setTopLevelData('name', v)}\n label=\"name\"\n />\n </FormRow>\n )}\n {!excludes.includes('scope') && (\n <FormRow label=\"Scope\">\n <Dropdown\n options={SCOPE_OPTIONS}\n disabled={formData.builtin}\n value={formData.scope || []}\n onChange={(v) => setTopLevelData('scope', v as AttributeModel['scope'])}\n multiSelect\n widthExpand\n />\n </FormRow>\n )}\n {!excludes.includes('type') && (\n <FormRow label=\"Type\">\n <Dropdown\n value={[formData?.data?.type]}\n disabled={formData.builtin}\n options={Object.values(TYPE_OPTIONS)}\n onChange={(v) => setData('type', v[0] as AttributeData['type'])}\n minSelected={1}\n widthExpand\n />\n </FormRow>\n )}\n {dataFields.map((field) => {\n // skip if field is excluded\n if (excludes.includes(field)) return null\n\n let fieldComp = null\n let fieldLabel = upperFirst(field)\n\n if (field === 'enum' || field === 'inherit') {\n const renderer = customFields[field as 'enum' | 'inherit']\n fieldComp = renderer(formData?.data[field], (value) => setData(field, value))\n } else if (field === 'default' && formData?.data?.type === 'boolean') {\n fieldComp = customFields['booleanDefault'](\n formData?.data[field] as boolean,\n (value) => setData(field, value as AttributeData['default']),\n )\n } else if (['ge', 'gt', 'le', 'lt'].includes(field)) {\n // ignore gt and lt\n if (['gt', 'lt'].includes(field)) return null\n fieldComp = (\n <MinMaxField\n value={formData?.data}\n isMin={field === 'ge'}\n isFloat={formData?.data?.type === 'float'}\n onChange={(v) => {\n const geValue = v.ge !== undefined ? Number(v.ge) : undefined\n const leValue = v.le !== undefined ? Number(v.le) : undefined\n\n if (\n // @ts-expect-error\n (v.ge !== undefined && isNaN(geValue)) ||\n // @ts-expect-error\n (v.le !== undefined && isNaN(leValue))\n ) {\n // Do not update the form if the value is not a valid number\n return\n }\n\n setFormData((d) => {\n if (!d || !d.data) return d\n const dt = { ...d.data, ...v }\n return { ...d, data: dt }\n })\n }}\n />\n )\n\n // rewrite field to min or max for display label\n fieldLabel = field === 'ge' ? 'Min' : 'Max'\n } else {\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const strValue = e.target.value\n switch (field) {\n case 'minLength':\n case 'maxLength':\n case 'minItems':\n case 'maxItems': {\n const num = parseInt(strValue, 10)\n setData(field, isNaN(num) ? undefined : num)\n break\n }\n default:\n // For string fields ('description', 'regex') or 'any' type fields ('example', 'default')\n setData(field, strValue as AttributeData[typeof field])\n break\n }\n }\n\n fieldComp = (\n <InputText\n value={String(formData?.data[field] ?? '')}\n onChange={handleInputChange}\n />\n )\n }\n\n return (\n <FormRow\n label={fieldLabel}\n key={field}\n style={{\n alignItems: 'flex-start',\n }}\n >\n {fieldComp}\n </FormRow>\n )\n })}\n <span>\n {(internalError || error) && (\n <span className=\"form-error-text\">{internalError || error}</span>\n )}\n </span>\n </FormLayout>\n )}\n </Dialog>\n )\n}\n"],"names":["useState","useEffect","jsxs","jsx","Button","Spacer","SaveButton","_a","EnumEditor","InputSwitch","camelCase","Dialog","FormLayout","FormRow","InputText","LockedInput","Dropdown","upperFirst","MinMaxField","_b"],"mappings":"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"}
@@ -138,7 +138,7 @@ const AttributeEditor = ({
138
138
  if (formData) {
139
139
  if (isNew) {
140
140
  if (existingNames.includes(formData.name)) internalError = "This attribute already exists";
141
- else if (!formData.name.match("^[a-zA-Z_]{2,20}$")) internalError = "Invalid attribute name";
141
+ else if (!formData.name.match("^[a-zA-Z_]{2,64}$")) error = "Invalid attribute name";
142
142
  }
143
143
  }
144
144
  const handleSubmit = () => {
@@ -1 +1 @@
1
- {"version":3,"file":"AttributeEditor.es.js","sources":["../../../../../src/components/AttributeEditor/AttributeEditor.tsx"],"sourcesContent":["import { FC, useEffect, useState } from 'react'\nimport {\n SaveButton,\n Spacer,\n FormLayout,\n FormRow,\n InputText,\n InputSwitch,\n LockedInput,\n Dropdown,\n Dialog,\n Button,\n} from '@ynput/ayon-react-components'\nimport { camelCase, upperFirst } from 'lodash'\nimport { MinMaxField } from './components'\nimport { EnumEditor } from '@shared/components/EnumEditor'\nimport { AttributeData, AttributeModel, AttributeEnumItem } from '@shared/api'\n\nconst SCOPE_OPTIONS = [\n { value: 'project', label: 'Project' },\n { value: 'folder', label: 'Folder' },\n { value: 'task', label: 'Task' },\n { value: 'product', label: 'Product' },\n { value: 'version', label: 'Version' },\n { value: 'representation', label: 'Representation' },\n { value: 'user', label: 'User' },\n]\n\n// Define types for constants\ninterface GlobalFieldEntry {\n value: keyof AttributeData\n scope: (AttributeModel['scope'] | '')[] | null\n}\n\nconst GLOBAL_FIELDS: GlobalFieldEntry[] = [\n { value: 'description', scope: null },\n { value: 'example', scope: null },\n // @ts-expect-error - project is not a scope?\n { value: 'default', scope: ['project'] },\n { value: 'inherit', scope: null },\n]\n\ninterface TypeOptionDef {\n value: AttributeData['type']\n label: string\n fields: (keyof AttributeData)[]\n exclude?: (keyof AttributeData)[]\n}\n\ninterface TypeOptionsMap {\n [key: string]: TypeOptionDef\n}\n\nconst TYPE_OPTIONS: TypeOptionsMap = {\n string: {\n value: 'string',\n label: 'String',\n fields: ['minLength', 'maxLength', 'enum', 'regex'],\n },\n integer: {\n value: 'integer',\n label: 'Integer',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n float: {\n value: 'float',\n label: 'Decimal number',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n list_of_strings: {\n value: 'list_of_strings',\n label: 'List Of Strings',\n fields: ['minItems', 'maxItems', 'enum'],\n },\n boolean: {\n value: 'boolean',\n label: 'Boolean',\n fields: [],\n exclude: ['example'],\n },\n}\n\ntype Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>\ntype PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\nexport type AttributeForm = PartialBy<AttributeModel, 'scope' | 'position'>\ntype Excludes = (keyof Omit<AttributeModel, 'data'> | keyof AttributeData)[]\n\nconst initFormData: AttributeForm = {\n name: '',\n scope: ['folder', 'task'],\n builtin: false,\n position: 0,\n data: {\n type: 'string',\n title: '',\n description: '',\n example: '',\n default: undefined,\n enum: [],\n minLength: undefined,\n maxLength: undefined,\n regex: '',\n minItems: undefined,\n maxItems: undefined,\n ge: undefined,\n gt: undefined,\n le: undefined,\n lt: undefined,\n },\n}\n\n// build the form data based on excludes and to update any data\nconst buildInitFormData = (excludes: Excludes, data?: Partial<AttributeForm>) => {\n // Create a deep clone of init form data\n const formData = JSON.parse(JSON.stringify(initFormData)) as AttributeForm\n\n // Filter out top-level excludes if not in required\n const required = ['name']\n Object.keys(formData).forEach((key) => {\n if (\n !required.includes(key) &&\n excludes.includes(key as keyof Omit<AttributeModel, 'data'>) &&\n key !== 'data'\n ) {\n delete formData[key as keyof AttributeForm]\n }\n })\n\n // Filter out data field excludes if not in in required\n const requiredData = ['title']\n if (formData.data) {\n Object.keys(formData.data).forEach((key) => {\n if (!requiredData.includes(key) && excludes.includes(key as keyof AttributeData)) {\n delete formData.data[key as keyof AttributeData]\n }\n })\n }\n\n // Merge with provided data if any\n if (data) {\n // Merge top-level fields\n Object.keys(data).forEach((key) => {\n const typedKey = key as keyof AttributeForm\n if (typedKey !== 'data' && excludes.includes(typedKey)) return\n\n if (typedKey === 'data' && data.data && formData.data) {\n // Deep merge of data fields\n formData.data = { ...formData.data, ...data.data }\n } else if (data[typedKey] !== undefined) {\n // @ts-ignore - We know these properties exist\n formData[typedKey] = data[typedKey]\n }\n })\n }\n\n return formData\n}\n\nexport interface AttributeEditorProps {\n attribute: AttributeForm | null\n existingNames: string[]\n error?: string\n isUpdating?: boolean\n excludes?: Excludes\n onHide: () => void\n onEdit: (attribute: AttributeForm) => void\n onDelete?: () => void\n}\n\nexport const AttributeEditor: FC<AttributeEditorProps> = ({\n attribute,\n existingNames,\n error = '',\n isUpdating,\n excludes = [],\n onHide,\n onEdit,\n onDelete,\n}) => {\n const initForm = buildInitFormData(excludes, { position: existingNames.length })\n const [formData, setFormData] = useState<AttributeForm | null>(attribute || initForm)\n\n useEffect(() => {\n if (!!attribute) setFormData(attribute)\n }, [attribute])\n\n const isNew = !attribute\n\n // const setTopLevelData = (key: string, value: string) => {\n const setTopLevelData = <K extends keyof Omit<AttributeModel, 'data'>>(\n key: K,\n value: AttributeModel[K],\n ) => {\n setFormData((d) => {\n if (!d) {\n return d\n }\n return { ...d, [key]: value }\n })\n }\n\n // const setData = (key, value) => {\n const setData = <K extends keyof AttributeData>(key: K, value: AttributeData[K]) => {\n setFormData((d) => {\n // Add a check for d and d.data\n if (!d || !d.data) {\n return d\n }\n const dt = { ...d.data, [key]: value }\n return { ...d, data: dt }\n })\n }\n\n let internalError = ''\n if (formData) {\n if (isNew) {\n if (existingNames.includes(formData.name)) internalError = 'This attribute already exists'\n else if (!formData.name.match('^[a-zA-Z_]{2,20}$')) internalError = 'Invalid attribute name'\n } // name validation\n }\n\n const handleSubmit = () => {\n if (formData) {\n onEdit(formData)\n }\n }\n\n const footer = (\n <div style={{ display: 'flex', width: '100%', flexDirection: 'row' }}>\n {onDelete && attribute && (\n <Button\n variant=\"danger\"\n label={'Delete attribute'}\n icon={'delete'}\n disabled={isUpdating}\n onClick={onDelete}\n />\n )}\n <Spacer />\n <SaveButton\n label={isNew ? 'Create Attribute' : 'Save Attribute'}\n icon={'check'}\n disabled={!!internalError || !formData}\n active={!internalError && !!formData}\n saving={isUpdating}\n onClick={handleSubmit}\n />\n </div>\n )\n\n let dataFields: (keyof AttributeData)[] = []\n\n // add global fields, only if scope are null (all) or the scope is included\n GLOBAL_FIELDS.forEach((globalField) => {\n // @ts-expect-error - project scope will never be found here?\n if (!globalField?.scope || globalField?.scope?.some((s) => formData?.scope?.includes(s))) {\n dataFields.push(globalField.value)\n }\n })\n\n if (formData?.data.type && TYPE_OPTIONS[formData.data.type]) {\n const typeOpt = TYPE_OPTIONS[formData.data.type]\n dataFields = [...dataFields, ...typeOpt.fields].filter((f) => !typeOpt.exclude?.includes(f))\n }\n\n type CustomFieldRenderer = (value: any, onChange: (newValue: any) => void) => JSX.Element | null\n const customFields: {\n enum: CustomFieldRenderer\n inherit: CustomFieldRenderer\n booleanDefault: CustomFieldRenderer\n } = {\n enum: (value = [], onChange) => (\n <EnumEditor\n values={value as AttributeEnumItem[]}\n onChange={(val) => {\n onChange(val)\n }}\n />\n ),\n inherit: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n booleanDefault: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n }\n\n const handleTitleChange = (e: React.ChangeEvent) => {\n const v = (e.target as HTMLInputElement).value\n setData('title', v)\n\n if (isNew) {\n setTopLevelData('name', camelCase(v))\n }\n }\n\n return (\n <Dialog\n header={formData?.data?.title || formData?.name || 'New attribute'}\n footer={footer}\n onClose={onHide}\n isOpen={true}\n style={{ width: 700, zIndex: 999 }}\n size=\"full\"\n onKeyDown={(e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault()\n handleSubmit()\n }\n }}\n >\n {formData && (\n <FormLayout>\n {!excludes.includes('title') && (\n <FormRow label={'Title'} key={'title'}>\n <InputText value={formData?.data['title']} onChange={handleTitleChange} autoFocus />\n </FormRow>\n )}\n {!excludes.includes('name') && (\n <FormRow label={'Name'} key={'name'}>\n <LockedInput\n value={formData.name}\n disabled={!isNew}\n onSubmit={(v) => setTopLevelData('name', v)}\n label=\"name\"\n />\n </FormRow>\n )}\n {!excludes.includes('scope') && (\n <FormRow label=\"Scope\">\n <Dropdown\n options={SCOPE_OPTIONS}\n disabled={formData.builtin}\n value={formData.scope || []}\n onChange={(v) => setTopLevelData('scope', v as AttributeModel['scope'])}\n multiSelect\n widthExpand\n />\n </FormRow>\n )}\n {!excludes.includes('type') && (\n <FormRow label=\"Type\">\n <Dropdown\n value={[formData?.data?.type]}\n disabled={formData.builtin}\n options={Object.values(TYPE_OPTIONS)}\n onChange={(v) => setData('type', v[0] as AttributeData['type'])}\n minSelected={1}\n widthExpand\n />\n </FormRow>\n )}\n {dataFields.map((field) => {\n // skip if field is excluded\n if (excludes.includes(field)) return null\n\n let fieldComp = null\n let fieldLabel = upperFirst(field)\n\n if (field === 'enum' || field === 'inherit') {\n const renderer = customFields[field as 'enum' | 'inherit']\n fieldComp = renderer(formData?.data[field], (value) => setData(field, value))\n } else if (field === 'default' && formData?.data?.type === 'boolean') {\n fieldComp = customFields['booleanDefault'](\n formData?.data[field] as boolean,\n (value) => setData(field, value as AttributeData['default']),\n )\n } else if (['ge', 'gt', 'le', 'lt'].includes(field)) {\n // ignore gt and lt\n if (['gt', 'lt'].includes(field)) return null\n fieldComp = (\n <MinMaxField\n value={formData?.data}\n isMin={field === 'ge'}\n isFloat={formData?.data?.type === 'float'}\n onChange={(v) => {\n const geValue = v.ge !== undefined ? Number(v.ge) : undefined\n const leValue = v.le !== undefined ? Number(v.le) : undefined\n\n if (\n // @ts-expect-error\n (v.ge !== undefined && isNaN(geValue)) ||\n // @ts-expect-error\n (v.le !== undefined && isNaN(leValue))\n ) {\n // Do not update the form if the value is not a valid number\n return\n }\n\n setFormData((d) => {\n if (!d || !d.data) return d\n const dt = { ...d.data, ...v }\n return { ...d, data: dt }\n })\n }}\n />\n )\n\n // rewrite field to min or max for display label\n fieldLabel = field === 'ge' ? 'Min' : 'Max'\n } else {\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const strValue = e.target.value\n switch (field) {\n case 'minLength':\n case 'maxLength':\n case 'minItems':\n case 'maxItems': {\n const num = parseInt(strValue, 10)\n setData(field, isNaN(num) ? undefined : num)\n break\n }\n default:\n // For string fields ('description', 'regex') or 'any' type fields ('example', 'default')\n setData(field, strValue as AttributeData[typeof field])\n break\n }\n }\n\n fieldComp = (\n <InputText\n value={String(formData?.data[field] ?? '')}\n onChange={handleInputChange}\n />\n )\n }\n\n return (\n <FormRow\n label={fieldLabel}\n key={field}\n style={{\n alignItems: 'flex-start',\n }}\n >\n {fieldComp}\n </FormRow>\n )\n })}\n <span>\n {(internalError || error) && (\n <span className=\"form-error-text\">{internalError || error}</span>\n )}\n </span>\n </FormLayout>\n )}\n </Dialog>\n )\n}\n"],"names":["jsxs","jsx","_a","_b"],"mappings":"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"}
1
+ {"version":3,"file":"AttributeEditor.es.js","sources":["../../../../../src/components/AttributeEditor/AttributeEditor.tsx"],"sourcesContent":["import { FC, useEffect, useState } from 'react'\nimport {\n SaveButton,\n Spacer,\n FormLayout,\n FormRow,\n InputText,\n InputSwitch,\n LockedInput,\n Dropdown,\n Dialog,\n Button,\n} from '@ynput/ayon-react-components'\nimport { camelCase, upperFirst } from 'lodash'\nimport { MinMaxField } from './components'\nimport { EnumEditor } from '@shared/components/EnumEditor'\nimport { AttributeData, AttributeModel, AttributeEnumItem } from '@shared/api'\n\nconst SCOPE_OPTIONS = [\n { value: 'project', label: 'Project' },\n { value: 'folder', label: 'Folder' },\n { value: 'task', label: 'Task' },\n { value: 'product', label: 'Product' },\n { value: 'version', label: 'Version' },\n { value: 'representation', label: 'Representation' },\n { value: 'user', label: 'User' },\n]\n\n// Define types for constants\ninterface GlobalFieldEntry {\n value: keyof AttributeData\n scope: (AttributeModel['scope'] | '')[] | null\n}\n\nconst GLOBAL_FIELDS: GlobalFieldEntry[] = [\n { value: 'description', scope: null },\n { value: 'example', scope: null },\n // @ts-expect-error - project is not a scope?\n { value: 'default', scope: ['project'] },\n { value: 'inherit', scope: null },\n]\n\ninterface TypeOptionDef {\n value: AttributeData['type']\n label: string\n fields: (keyof AttributeData)[]\n exclude?: (keyof AttributeData)[]\n}\n\ninterface TypeOptionsMap {\n [key: string]: TypeOptionDef\n}\n\nconst TYPE_OPTIONS: TypeOptionsMap = {\n string: {\n value: 'string',\n label: 'String',\n fields: ['minLength', 'maxLength', 'enum', 'regex'],\n },\n integer: {\n value: 'integer',\n label: 'Integer',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n float: {\n value: 'float',\n label: 'Decimal number',\n fields: ['ge', 'gt', 'le', 'lt'],\n },\n list_of_strings: {\n value: 'list_of_strings',\n label: 'List Of Strings',\n fields: ['minItems', 'maxItems', 'enum'],\n },\n boolean: {\n value: 'boolean',\n label: 'Boolean',\n fields: [],\n exclude: ['example'],\n },\n}\n\ntype Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>\ntype PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>\nexport type AttributeForm = PartialBy<AttributeModel, 'scope' | 'position'>\ntype Excludes = (keyof Omit<AttributeModel, 'data'> | keyof AttributeData)[]\n\nconst initFormData: AttributeForm = {\n name: '',\n scope: ['folder', 'task'],\n builtin: false,\n position: 0,\n data: {\n type: 'string',\n title: '',\n description: '',\n example: '',\n default: undefined,\n enum: [],\n minLength: undefined,\n maxLength: undefined,\n regex: '',\n minItems: undefined,\n maxItems: undefined,\n ge: undefined,\n gt: undefined,\n le: undefined,\n lt: undefined,\n },\n}\n\n// build the form data based on excludes and to update any data\nconst buildInitFormData = (excludes: Excludes, data?: Partial<AttributeForm>) => {\n // Create a deep clone of init form data\n const formData = JSON.parse(JSON.stringify(initFormData)) as AttributeForm\n\n // Filter out top-level excludes if not in required\n const required = ['name']\n Object.keys(formData).forEach((key) => {\n if (\n !required.includes(key) &&\n excludes.includes(key as keyof Omit<AttributeModel, 'data'>) &&\n key !== 'data'\n ) {\n delete formData[key as keyof AttributeForm]\n }\n })\n\n // Filter out data field excludes if not in in required\n const requiredData = ['title']\n if (formData.data) {\n Object.keys(formData.data).forEach((key) => {\n if (!requiredData.includes(key) && excludes.includes(key as keyof AttributeData)) {\n delete formData.data[key as keyof AttributeData]\n }\n })\n }\n\n // Merge with provided data if any\n if (data) {\n // Merge top-level fields\n Object.keys(data).forEach((key) => {\n const typedKey = key as keyof AttributeForm\n if (typedKey !== 'data' && excludes.includes(typedKey)) return\n\n if (typedKey === 'data' && data.data && formData.data) {\n // Deep merge of data fields\n formData.data = { ...formData.data, ...data.data }\n } else if (data[typedKey] !== undefined) {\n // @ts-ignore - We know these properties exist\n formData[typedKey] = data[typedKey]\n }\n })\n }\n\n return formData\n}\n\nexport interface AttributeEditorProps {\n attribute: AttributeForm | null\n existingNames: string[]\n error?: string\n isUpdating?: boolean\n excludes?: Excludes\n onHide: () => void\n onEdit: (attribute: AttributeForm) => void\n onDelete?: () => void\n}\n\nexport const AttributeEditor: FC<AttributeEditorProps> = ({\n attribute,\n existingNames,\n error = '',\n isUpdating,\n excludes = [],\n onHide,\n onEdit,\n onDelete,\n}) => {\n const initForm = buildInitFormData(excludes, { position: existingNames.length })\n const [formData, setFormData] = useState<AttributeForm | null>(attribute || initForm)\n\n useEffect(() => {\n if (!!attribute) setFormData(attribute)\n }, [attribute])\n\n const isNew = !attribute\n\n // const setTopLevelData = (key: string, value: string) => {\n const setTopLevelData = <K extends keyof Omit<AttributeModel, 'data'>>(\n key: K,\n value: AttributeModel[K],\n ) => {\n setFormData((d) => {\n if (!d) {\n return d\n }\n return { ...d, [key]: value }\n })\n }\n\n // const setData = (key, value) => {\n const setData = <K extends keyof AttributeData>(key: K, value: AttributeData[K]) => {\n setFormData((d) => {\n // Add a check for d and d.data\n if (!d || !d.data) {\n return d\n }\n const dt = { ...d.data, [key]: value }\n return { ...d, data: dt }\n })\n }\n\n let internalError = ''\n if (formData) {\n if (isNew) {\n if (existingNames.includes(formData.name)) internalError = 'This attribute already exists'\n else if (!formData.name.match('^[a-zA-Z_]{2,64}$')) error = 'Invalid attribute name'\n } // name validation\n }\n\n const handleSubmit = () => {\n if (formData) {\n onEdit(formData)\n }\n }\n\n const footer = (\n <div style={{ display: 'flex', width: '100%', flexDirection: 'row' }}>\n {onDelete && attribute && (\n <Button\n variant=\"danger\"\n label={'Delete attribute'}\n icon={'delete'}\n disabled={isUpdating}\n onClick={onDelete}\n />\n )}\n <Spacer />\n <SaveButton\n label={isNew ? 'Create Attribute' : 'Save Attribute'}\n icon={'check'}\n disabled={!!internalError || !formData}\n active={!internalError && !!formData}\n saving={isUpdating}\n onClick={handleSubmit}\n />\n </div>\n )\n\n let dataFields: (keyof AttributeData)[] = []\n\n // add global fields, only if scope are null (all) or the scope is included\n GLOBAL_FIELDS.forEach((globalField) => {\n // @ts-expect-error - project scope will never be found here?\n if (!globalField?.scope || globalField?.scope?.some((s) => formData?.scope?.includes(s))) {\n dataFields.push(globalField.value)\n }\n })\n\n if (formData?.data.type && TYPE_OPTIONS[formData.data.type]) {\n const typeOpt = TYPE_OPTIONS[formData.data.type]\n dataFields = [...dataFields, ...typeOpt.fields].filter((f) => !typeOpt.exclude?.includes(f))\n }\n\n type CustomFieldRenderer = (value: any, onChange: (newValue: any) => void) => JSX.Element | null\n const customFields: {\n enum: CustomFieldRenderer\n inherit: CustomFieldRenderer\n booleanDefault: CustomFieldRenderer\n } = {\n enum: (value = [], onChange) => (\n <EnumEditor\n values={value as AttributeEnumItem[]}\n onChange={(val) => {\n onChange(val)\n }}\n />\n ),\n inherit: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n booleanDefault: (value, onChange) => (\n <InputSwitch\n checked={value}\n onChange={(e) => onChange((e.target as HTMLInputElement).checked)}\n />\n ),\n }\n\n const handleTitleChange = (e: React.ChangeEvent) => {\n const v = (e.target as HTMLInputElement).value\n setData('title', v)\n\n if (isNew) {\n setTopLevelData('name', camelCase(v))\n }\n }\n\n return (\n <Dialog\n header={formData?.data?.title || formData?.name || 'New attribute'}\n footer={footer}\n onClose={onHide}\n isOpen={true}\n style={{ width: 700, zIndex: 999 }}\n size=\"full\"\n onKeyDown={(e: React.KeyboardEvent<HTMLDivElement>) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {\n e.preventDefault()\n handleSubmit()\n }\n }}\n >\n {formData && (\n <FormLayout>\n {!excludes.includes('title') && (\n <FormRow label={'Title'} key={'title'}>\n <InputText value={formData?.data['title']} onChange={handleTitleChange} autoFocus />\n </FormRow>\n )}\n {!excludes.includes('name') && (\n <FormRow label={'Name'} key={'name'}>\n <LockedInput\n value={formData.name}\n disabled={!isNew}\n onSubmit={(v) => setTopLevelData('name', v)}\n label=\"name\"\n />\n </FormRow>\n )}\n {!excludes.includes('scope') && (\n <FormRow label=\"Scope\">\n <Dropdown\n options={SCOPE_OPTIONS}\n disabled={formData.builtin}\n value={formData.scope || []}\n onChange={(v) => setTopLevelData('scope', v as AttributeModel['scope'])}\n multiSelect\n widthExpand\n />\n </FormRow>\n )}\n {!excludes.includes('type') && (\n <FormRow label=\"Type\">\n <Dropdown\n value={[formData?.data?.type]}\n disabled={formData.builtin}\n options={Object.values(TYPE_OPTIONS)}\n onChange={(v) => setData('type', v[0] as AttributeData['type'])}\n minSelected={1}\n widthExpand\n />\n </FormRow>\n )}\n {dataFields.map((field) => {\n // skip if field is excluded\n if (excludes.includes(field)) return null\n\n let fieldComp = null\n let fieldLabel = upperFirst(field)\n\n if (field === 'enum' || field === 'inherit') {\n const renderer = customFields[field as 'enum' | 'inherit']\n fieldComp = renderer(formData?.data[field], (value) => setData(field, value))\n } else if (field === 'default' && formData?.data?.type === 'boolean') {\n fieldComp = customFields['booleanDefault'](\n formData?.data[field] as boolean,\n (value) => setData(field, value as AttributeData['default']),\n )\n } else if (['ge', 'gt', 'le', 'lt'].includes(field)) {\n // ignore gt and lt\n if (['gt', 'lt'].includes(field)) return null\n fieldComp = (\n <MinMaxField\n value={formData?.data}\n isMin={field === 'ge'}\n isFloat={formData?.data?.type === 'float'}\n onChange={(v) => {\n const geValue = v.ge !== undefined ? Number(v.ge) : undefined\n const leValue = v.le !== undefined ? Number(v.le) : undefined\n\n if (\n // @ts-expect-error\n (v.ge !== undefined && isNaN(geValue)) ||\n // @ts-expect-error\n (v.le !== undefined && isNaN(leValue))\n ) {\n // Do not update the form if the value is not a valid number\n return\n }\n\n setFormData((d) => {\n if (!d || !d.data) return d\n const dt = { ...d.data, ...v }\n return { ...d, data: dt }\n })\n }}\n />\n )\n\n // rewrite field to min or max for display label\n fieldLabel = field === 'ge' ? 'Min' : 'Max'\n } else {\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const strValue = e.target.value\n switch (field) {\n case 'minLength':\n case 'maxLength':\n case 'minItems':\n case 'maxItems': {\n const num = parseInt(strValue, 10)\n setData(field, isNaN(num) ? undefined : num)\n break\n }\n default:\n // For string fields ('description', 'regex') or 'any' type fields ('example', 'default')\n setData(field, strValue as AttributeData[typeof field])\n break\n }\n }\n\n fieldComp = (\n <InputText\n value={String(formData?.data[field] ?? '')}\n onChange={handleInputChange}\n />\n )\n }\n\n return (\n <FormRow\n label={fieldLabel}\n key={field}\n style={{\n alignItems: 'flex-start',\n }}\n >\n {fieldComp}\n </FormRow>\n )\n })}\n <span>\n {(internalError || error) && (\n <span className=\"form-error-text\">{internalError || error}</span>\n )}\n </span>\n </FormLayout>\n )}\n </Dialog>\n )\n}\n"],"names":["jsxs","jsx","_a","_b"],"mappings":"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"}
@@ -76,7 +76,6 @@ require("../../context/RemoteModulesContext.cjs.js");
76
76
  require("../../../../_virtual/runtime.cjs.js");
77
77
  require("../../../../_virtual/semver.cjs.js");
78
78
  const useEntityUpdate = require("../../hooks/useEntityUpdate.cjs.js");
79
- require("react-router-dom");
80
79
  require("custom-protocol-check");
81
80
  const visibleFields = [
82
81
  "id",
@@ -1 +1 @@
1
- {"version":3,"file":"DetailsPanelAttributes.cjs.js","sources":["../../../../../src/components/DetailsPanelAttributes/DetailsPanelAttributes.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react'\nimport { Section } from '@ynput/ayon-react-components'\nimport { getMixedState } from '@shared/util'\nimport { useGetSiteInfoQuery, useGetProjectQuery } from '@shared/api'\nimport {\n DetailsPanelAttributesEditor,\n AttributeField,\n DetailsPanelAttributesEditorProps,\n} from './DetailsPanelAttributesEditor'\nimport { useEntityUpdate } from '@shared/hooks'\nimport { upperFirst } from 'lodash'\nimport type { DetailsPanelEntityData } from '@shared/api'\n\ntype EntityForm = {\n id: string\n name: string\n label: string | null | undefined\n entityType: 'folder' | 'task' | 'product' | 'version'\n taskType?: string\n folderType?: string\n productType?: string\n tags: string[]\n status: string\n updatedAt: string\n createdAt: string\n projectName: string\n path: string\n // attribs\n [key: string]: string | number | boolean | Date | any[] | Record<string, any> | undefined | null\n}\n\n// all fields in entity form are visible\nconst visibleFields: Array<keyof EntityForm> = [\n 'id',\n 'name',\n 'label',\n 'entityType',\n 'taskType',\n 'folderType',\n 'productType',\n 'tags',\n 'status',\n 'updatedAt',\n 'createdAt',\n 'projectName',\n 'path',\n]\n\nconst readOnlyFields: Array<keyof EntityForm> = [\n 'id',\n 'entityType',\n 'projectName',\n 'path',\n 'name',\n 'createdAt',\n 'updatedAt',\n]\n\nexport type DetailsPanelAttributesProps = {\n entities: DetailsPanelEntityData[]\n isLoading: boolean\n}\n\nexport const DetailsPanelAttributes = ({\n entities = [],\n isLoading,\n}: DetailsPanelAttributesProps) => {\n // form for project data\n const [mixedFields, setMixedFields] = useState<string[]>([])\n const [formData, setFormData] = useState<EntityForm | null>(null)\n\n const buildInitialForm = () => {\n // Group entity values by field name\n const valuesByField: Record<string, any[]> = {}\n // Track which fields have mixed values\n const mixedFieldsSet = new Set<string>()\n\n entities.forEach((entity) => {\n const mappedEntity: EntityForm = {\n id: entity.id,\n name: entity.name,\n label: entity.label,\n entityType: entity.entityType as 'folder' | 'task' | 'product' | 'version',\n createdAt: entity.createdAt,\n updatedAt: entity.updatedAt,\n projectName: entity.projectName,\n status: entity.status,\n tags: entity.tags || [],\n path: entity.folder?.path || '',\n folderType: entity.folder?.folderType,\n productType: entity.product?.productType,\n taskType: entity.task?.taskType,\n }\n\n // Process regular fields\n Object.keys(mappedEntity).forEach((key) => {\n if (visibleFields.includes(key as keyof EntityForm)) {\n valuesByField[key] = valuesByField[key] || []\n valuesByField[key].push((mappedEntity as any)[key])\n }\n })\n\n // Process attrib fields\n if (entity.attrib) {\n Object.keys(entity.attrib).forEach((key) => {\n const attribKey = `attrib.${key}`\n valuesByField[attribKey] = valuesByField[attribKey] || []\n valuesByField[attribKey].push(entity.attrib?.[key])\n })\n }\n })\n\n // Apply getMixedState to each field\n const formData = Object.entries(valuesByField).reduce((result, [key, values]) => {\n const { value, isMixed } = getMixedState(values)\n result[key] = value\n\n // Add to mixedFields if this field has mixed values\n if (isMixed) {\n mixedFieldsSet.add(key)\n }\n\n return result\n }, {} as Record<string, any>)\n\n setFormData(formData as EntityForm)\n // Update the mixedFields state with all fields that have mixed values\n setMixedFields(Array.from(mixedFieldsSet))\n }\n\n useEffect(() => {\n if (isLoading || entities.length === 0) return\n buildInitialForm()\n }, [entities, isLoading])\n\n const { data: projectData } = useGetProjectQuery(\n { projectName: formData?.projectName || '' },\n { skip: !formData?.projectName || mixedFields.includes('projectName') },\n )\n const { folderTypes = [], taskTypes = [], statuses = [], tags = [] } = projectData || {}\n\n const { data: info } = useGetSiteInfoQuery({ full: true })\n const { attributes = [] } = info || {}\n\n // build the fields array for defining the schema\n const fields: AttributeField[] = useMemo(() => {\n // Create custom fields as proper AttributeModel objects\n const customFieldsData: AttributeField[] = [\n {\n name: 'label',\n data: {\n type: 'string',\n title: 'Label',\n description: 'Used as a nice visual label only',\n },\n },\n {\n name: 'folderType',\n hidden: formData?.entityType !== 'folder',\n data: {\n type: 'string',\n title: 'Folder Type',\n description: 'Type of the folder',\n enum: folderTypes.map((type) => ({\n value: type.name,\n label: type.name,\n icon: type.icon,\n })),\n },\n },\n {\n name: 'taskType',\n hidden: formData?.entityType !== 'task',\n data: {\n type: 'string',\n title: 'Task Type',\n description: 'Type of the task',\n enum: taskTypes.map((type) => ({\n value: type.name,\n label: type.name,\n icon: type.icon,\n })),\n },\n },\n {\n name: 'status',\n data: {\n type: 'string',\n title: 'Status',\n description: 'The state the entity is in, is it approved or in progress etc.',\n enum: statuses.map((status) => ({\n value: status.name,\n label: status.name,\n icon: status.icon,\n color: status.color,\n })),\n },\n },\n {\n name: 'tags',\n data: {\n type: 'list_of_strings',\n title: 'Tags',\n enum: tags.map((tag) => ({\n value: tag.name,\n label: tag.name,\n color: tag.color,\n })),\n },\n },\n ]\n\n // Filter API attributes based on entity type\n const apiAttributesData: AttributeField[] = formData?.entityType\n ? attributes\n .filter((attr) => attr.scope?.includes(formData.entityType))\n .map((attr) => ({\n name: 'attrib.' + attr.name,\n data: attr.data,\n }))\n : []\n\n const readOnlyFieldsData: AttributeField[] = readOnlyFields.map((field) => ({\n name: field as string,\n readonly: true,\n data: {\n type: 'string',\n title: upperFirst(field as string),\n },\n }))\n\n // Combine custom fields with API attributes\n const allFieldsData = [...customFieldsData, ...apiAttributesData, ...readOnlyFieldsData]\n const sortToTop = ['path', 'name']\n const sortedFieldsData = [...allFieldsData].sort((a, b) => {\n const aIndex = sortToTop.indexOf(a.name)\n const bIndex = sortToTop.indexOf(b.name)\n if (aIndex === -1 && bIndex === -1) return 0\n if (aIndex === -1) return 1\n if (bIndex === -1) return -1\n return aIndex - bIndex\n })\n\n return sortedFieldsData\n }, [attributes, folderTypes, taskTypes, statuses, tags, formData?.entityType])\n\n //\n let enableEditing = false\n if (\n ['task', 'folder'].includes(formData?.entityType || '') &&\n !mixedFields.includes('projectName')\n ) {\n enableEditing = true\n }\n\n const entityType = formData?.entityType || 'task'\n const projectName = formData?.projectName || ''\n\n // Setup entity update functionality\n const { updateEntity } = useEntityUpdate({\n entities: entities.map((entity) => ({\n id: entity.id,\n projectName: entity.projectName || '',\n folderId: entity.folder?.id,\n users: entity.task?.assignees || [],\n })),\n entityType,\n })\n\n const handleChange: DetailsPanelAttributesEditorProps['onChange'] = (key, value) => {\n if (key.startsWith('attrib.')) {\n value = {\n [key.replace('attrib.', '')]: value,\n }\n key = 'attrib'\n }\n\n console.log('handleChange', key, value)\n\n // update the form data\n // @ts-ignore\n setFormData((prev) => ({\n ...prev,\n [key]: value,\n }))\n\n // update the entity in database\n updateEntity(key, value)\n }\n\n return (\n <Section style={{ padding: 8, overflow: 'hidden' }}>\n <DetailsPanelAttributesEditor\n fields={fields}\n form={formData || {}}\n mixedFields={mixedFields}\n isLoading={isLoading}\n enableEditing={enableEditing}\n onChange={handleChange}\n />\n </Section>\n )\n}\n"],"names":["useState","_a","formData","getMixedState","useEffect","useGetProjectQuery","useGetSiteInfoQuery","useMemo","upperFirst","useEntityUpdate","jsx","Section","DetailsPanelAttributesEditor"],"mappings":"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"}
1
+ {"version":3,"file":"DetailsPanelAttributes.cjs.js","sources":["../../../../../src/components/DetailsPanelAttributes/DetailsPanelAttributes.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react'\nimport { Section } from '@ynput/ayon-react-components'\nimport { getMixedState } from '@shared/util'\nimport { useGetSiteInfoQuery, useGetProjectQuery } from '@shared/api'\nimport {\n DetailsPanelAttributesEditor,\n AttributeField,\n DetailsPanelAttributesEditorProps,\n} from './DetailsPanelAttributesEditor'\nimport { useEntityUpdate } from '@shared/hooks'\nimport { upperFirst } from 'lodash'\nimport type { DetailsPanelEntityData } from '@shared/api'\n\ntype EntityForm = {\n id: string\n name: string\n label: string | null | undefined\n entityType: 'folder' | 'task' | 'product' | 'version'\n taskType?: string\n folderType?: string\n productType?: string\n tags: string[]\n status: string\n updatedAt: string\n createdAt: string\n projectName: string\n path: string\n // attribs\n [key: string]: string | number | boolean | Date | any[] | Record<string, any> | undefined | null\n}\n\n// all fields in entity form are visible\nconst visibleFields: Array<keyof EntityForm> = [\n 'id',\n 'name',\n 'label',\n 'entityType',\n 'taskType',\n 'folderType',\n 'productType',\n 'tags',\n 'status',\n 'updatedAt',\n 'createdAt',\n 'projectName',\n 'path',\n]\n\nconst readOnlyFields: Array<keyof EntityForm> = [\n 'id',\n 'entityType',\n 'projectName',\n 'path',\n 'name',\n 'createdAt',\n 'updatedAt',\n]\n\nexport type DetailsPanelAttributesProps = {\n entities: DetailsPanelEntityData[]\n isLoading: boolean\n}\n\nexport const DetailsPanelAttributes = ({\n entities = [],\n isLoading,\n}: DetailsPanelAttributesProps) => {\n // form for project data\n const [mixedFields, setMixedFields] = useState<string[]>([])\n const [formData, setFormData] = useState<EntityForm | null>(null)\n\n const buildInitialForm = () => {\n // Group entity values by field name\n const valuesByField: Record<string, any[]> = {}\n // Track which fields have mixed values\n const mixedFieldsSet = new Set<string>()\n\n entities.forEach((entity) => {\n const mappedEntity: EntityForm = {\n id: entity.id,\n name: entity.name,\n label: entity.label,\n entityType: entity.entityType as 'folder' | 'task' | 'product' | 'version',\n createdAt: entity.createdAt,\n updatedAt: entity.updatedAt,\n projectName: entity.projectName,\n status: entity.status,\n tags: entity.tags || [],\n path: entity.folder?.path || '',\n folderType: entity.folder?.folderType,\n productType: entity.product?.productType,\n taskType: entity.task?.taskType,\n }\n\n // Process regular fields\n Object.keys(mappedEntity).forEach((key) => {\n if (visibleFields.includes(key as keyof EntityForm)) {\n valuesByField[key] = valuesByField[key] || []\n valuesByField[key].push((mappedEntity as any)[key])\n }\n })\n\n // Process attrib fields\n if (entity.attrib) {\n Object.keys(entity.attrib).forEach((key) => {\n const attribKey = `attrib.${key}`\n valuesByField[attribKey] = valuesByField[attribKey] || []\n valuesByField[attribKey].push(entity.attrib?.[key])\n })\n }\n })\n\n // Apply getMixedState to each field\n const formData = Object.entries(valuesByField).reduce((result, [key, values]) => {\n const { value, isMixed } = getMixedState(values)\n result[key] = value\n\n // Add to mixedFields if this field has mixed values\n if (isMixed) {\n mixedFieldsSet.add(key)\n }\n\n return result\n }, {} as Record<string, any>)\n\n setFormData(formData as EntityForm)\n // Update the mixedFields state with all fields that have mixed values\n setMixedFields(Array.from(mixedFieldsSet))\n }\n\n useEffect(() => {\n if (isLoading || entities.length === 0) return\n buildInitialForm()\n }, [entities, isLoading])\n\n const { data: projectData } = useGetProjectQuery(\n { projectName: formData?.projectName || '' },\n { skip: !formData?.projectName || mixedFields.includes('projectName') },\n )\n const { folderTypes = [], taskTypes = [], statuses = [], tags = [] } = projectData || {}\n\n const { data: info } = useGetSiteInfoQuery({ full: true })\n const { attributes = [] } = info || {}\n\n // build the fields array for defining the schema\n const fields: AttributeField[] = useMemo(() => {\n // Create custom fields as proper AttributeModel objects\n const customFieldsData: AttributeField[] = [\n {\n name: 'label',\n data: {\n type: 'string',\n title: 'Label',\n description: 'Used as a nice visual label only',\n },\n },\n {\n name: 'folderType',\n hidden: formData?.entityType !== 'folder',\n data: {\n type: 'string',\n title: 'Folder Type',\n description: 'Type of the folder',\n enum: folderTypes.map((type) => ({\n value: type.name,\n label: type.name,\n icon: type.icon,\n })),\n },\n },\n {\n name: 'taskType',\n hidden: formData?.entityType !== 'task',\n data: {\n type: 'string',\n title: 'Task Type',\n description: 'Type of the task',\n enum: taskTypes.map((type) => ({\n value: type.name,\n label: type.name,\n icon: type.icon,\n })),\n },\n },\n {\n name: 'status',\n data: {\n type: 'string',\n title: 'Status',\n description: 'The state the entity is in, is it approved or in progress etc.',\n enum: statuses.map((status) => ({\n value: status.name,\n label: status.name,\n icon: status.icon,\n color: status.color,\n })),\n },\n },\n {\n name: 'tags',\n data: {\n type: 'list_of_strings',\n title: 'Tags',\n enum: tags.map((tag) => ({\n value: tag.name,\n label: tag.name,\n color: tag.color,\n })),\n },\n },\n ]\n\n // Filter API attributes based on entity type\n const apiAttributesData: AttributeField[] = formData?.entityType\n ? attributes\n .filter((attr) => attr.scope?.includes(formData.entityType))\n .map((attr) => ({\n name: 'attrib.' + attr.name,\n data: attr.data,\n }))\n : []\n\n const readOnlyFieldsData: AttributeField[] = readOnlyFields.map((field) => ({\n name: field as string,\n readonly: true,\n data: {\n type: 'string',\n title: upperFirst(field as string),\n },\n }))\n\n // Combine custom fields with API attributes\n const allFieldsData = [...customFieldsData, ...apiAttributesData, ...readOnlyFieldsData]\n const sortToTop = ['path', 'name']\n const sortedFieldsData = [...allFieldsData].sort((a, b) => {\n const aIndex = sortToTop.indexOf(a.name)\n const bIndex = sortToTop.indexOf(b.name)\n if (aIndex === -1 && bIndex === -1) return 0\n if (aIndex === -1) return 1\n if (bIndex === -1) return -1\n return aIndex - bIndex\n })\n\n return sortedFieldsData\n }, [attributes, folderTypes, taskTypes, statuses, tags, formData?.entityType])\n\n //\n let enableEditing = false\n if (\n ['task', 'folder'].includes(formData?.entityType || '') &&\n !mixedFields.includes('projectName')\n ) {\n enableEditing = true\n }\n\n const entityType = formData?.entityType || 'task'\n const projectName = formData?.projectName || ''\n\n // Setup entity update functionality\n const { updateEntity } = useEntityUpdate({\n entities: entities.map((entity) => ({\n id: entity.id,\n projectName: entity.projectName || '',\n folderId: entity.folder?.id,\n users: entity.task?.assignees || [],\n })),\n entityType,\n })\n\n const handleChange: DetailsPanelAttributesEditorProps['onChange'] = (key, value) => {\n if (key.startsWith('attrib.')) {\n value = {\n [key.replace('attrib.', '')]: value,\n }\n key = 'attrib'\n }\n\n console.log('handleChange', key, value)\n\n // update the form data\n // @ts-ignore\n setFormData((prev) => ({\n ...prev,\n [key]: value,\n }))\n\n // update the entity in database\n updateEntity(key, value)\n }\n\n return (\n <Section style={{ padding: 8, overflow: 'hidden' }}>\n <DetailsPanelAttributesEditor\n fields={fields}\n form={formData || {}}\n mixedFields={mixedFields}\n isLoading={isLoading}\n enableEditing={enableEditing}\n onChange={handleChange}\n />\n </Section>\n )\n}\n"],"names":["useState","_a","formData","getMixedState","useEffect","useGetProjectQuery","useGetSiteInfoQuery","useMemo","upperFirst","useEntityUpdate","jsx","Section","DetailsPanelAttributesEditor"],"mappings":"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"}
@@ -74,7 +74,6 @@ import "../../context/RemoteModulesContext.es.js";
74
74
  import "../../../../_virtual/runtime.es.js";
75
75
  import "../../../../_virtual/semver.es.js";
76
76
  import { useEntityUpdate } from "../../hooks/useEntityUpdate.es.js";
77
- import "react-router-dom";
78
77
  import "custom-protocol-check";
79
78
  const visibleFields = [
80
79
  "id",
@@ -1 +1 @@
1
- {"version":3,"file":"DetailsPanelAttributes.es.js","sources":["../../../../../src/components/DetailsPanelAttributes/DetailsPanelAttributes.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react'\nimport { Section } from '@ynput/ayon-react-components'\nimport { getMixedState } from '@shared/util'\nimport { useGetSiteInfoQuery, useGetProjectQuery } from '@shared/api'\nimport {\n DetailsPanelAttributesEditor,\n AttributeField,\n DetailsPanelAttributesEditorProps,\n} from './DetailsPanelAttributesEditor'\nimport { useEntityUpdate } from '@shared/hooks'\nimport { upperFirst } from 'lodash'\nimport type { DetailsPanelEntityData } from '@shared/api'\n\ntype EntityForm = {\n id: string\n name: string\n label: string | null | undefined\n entityType: 'folder' | 'task' | 'product' | 'version'\n taskType?: string\n folderType?: string\n productType?: string\n tags: string[]\n status: string\n updatedAt: string\n createdAt: string\n projectName: string\n path: string\n // attribs\n [key: string]: string | number | boolean | Date | any[] | Record<string, any> | undefined | null\n}\n\n// all fields in entity form are visible\nconst visibleFields: Array<keyof EntityForm> = [\n 'id',\n 'name',\n 'label',\n 'entityType',\n 'taskType',\n 'folderType',\n 'productType',\n 'tags',\n 'status',\n 'updatedAt',\n 'createdAt',\n 'projectName',\n 'path',\n]\n\nconst readOnlyFields: Array<keyof EntityForm> = [\n 'id',\n 'entityType',\n 'projectName',\n 'path',\n 'name',\n 'createdAt',\n 'updatedAt',\n]\n\nexport type DetailsPanelAttributesProps = {\n entities: DetailsPanelEntityData[]\n isLoading: boolean\n}\n\nexport const DetailsPanelAttributes = ({\n entities = [],\n isLoading,\n}: DetailsPanelAttributesProps) => {\n // form for project data\n const [mixedFields, setMixedFields] = useState<string[]>([])\n const [formData, setFormData] = useState<EntityForm | null>(null)\n\n const buildInitialForm = () => {\n // Group entity values by field name\n const valuesByField: Record<string, any[]> = {}\n // Track which fields have mixed values\n const mixedFieldsSet = new Set<string>()\n\n entities.forEach((entity) => {\n const mappedEntity: EntityForm = {\n id: entity.id,\n name: entity.name,\n label: entity.label,\n entityType: entity.entityType as 'folder' | 'task' | 'product' | 'version',\n createdAt: entity.createdAt,\n updatedAt: entity.updatedAt,\n projectName: entity.projectName,\n status: entity.status,\n tags: entity.tags || [],\n path: entity.folder?.path || '',\n folderType: entity.folder?.folderType,\n productType: entity.product?.productType,\n taskType: entity.task?.taskType,\n }\n\n // Process regular fields\n Object.keys(mappedEntity).forEach((key) => {\n if (visibleFields.includes(key as keyof EntityForm)) {\n valuesByField[key] = valuesByField[key] || []\n valuesByField[key].push((mappedEntity as any)[key])\n }\n })\n\n // Process attrib fields\n if (entity.attrib) {\n Object.keys(entity.attrib).forEach((key) => {\n const attribKey = `attrib.${key}`\n valuesByField[attribKey] = valuesByField[attribKey] || []\n valuesByField[attribKey].push(entity.attrib?.[key])\n })\n }\n })\n\n // Apply getMixedState to each field\n const formData = Object.entries(valuesByField).reduce((result, [key, values]) => {\n const { value, isMixed } = getMixedState(values)\n result[key] = value\n\n // Add to mixedFields if this field has mixed values\n if (isMixed) {\n mixedFieldsSet.add(key)\n }\n\n return result\n }, {} as Record<string, any>)\n\n setFormData(formData as EntityForm)\n // Update the mixedFields state with all fields that have mixed values\n setMixedFields(Array.from(mixedFieldsSet))\n }\n\n useEffect(() => {\n if (isLoading || entities.length === 0) return\n buildInitialForm()\n }, [entities, isLoading])\n\n const { data: projectData } = useGetProjectQuery(\n { projectName: formData?.projectName || '' },\n { skip: !formData?.projectName || mixedFields.includes('projectName') },\n )\n const { folderTypes = [], taskTypes = [], statuses = [], tags = [] } = projectData || {}\n\n const { data: info } = useGetSiteInfoQuery({ full: true })\n const { attributes = [] } = info || {}\n\n // build the fields array for defining the schema\n const fields: AttributeField[] = useMemo(() => {\n // Create custom fields as proper AttributeModel objects\n const customFieldsData: AttributeField[] = [\n {\n name: 'label',\n data: {\n type: 'string',\n title: 'Label',\n description: 'Used as a nice visual label only',\n },\n },\n {\n name: 'folderType',\n hidden: formData?.entityType !== 'folder',\n data: {\n type: 'string',\n title: 'Folder Type',\n description: 'Type of the folder',\n enum: folderTypes.map((type) => ({\n value: type.name,\n label: type.name,\n icon: type.icon,\n })),\n },\n },\n {\n name: 'taskType',\n hidden: formData?.entityType !== 'task',\n data: {\n type: 'string',\n title: 'Task Type',\n description: 'Type of the task',\n enum: taskTypes.map((type) => ({\n value: type.name,\n label: type.name,\n icon: type.icon,\n })),\n },\n },\n {\n name: 'status',\n data: {\n type: 'string',\n title: 'Status',\n description: 'The state the entity is in, is it approved or in progress etc.',\n enum: statuses.map((status) => ({\n value: status.name,\n label: status.name,\n icon: status.icon,\n color: status.color,\n })),\n },\n },\n {\n name: 'tags',\n data: {\n type: 'list_of_strings',\n title: 'Tags',\n enum: tags.map((tag) => ({\n value: tag.name,\n label: tag.name,\n color: tag.color,\n })),\n },\n },\n ]\n\n // Filter API attributes based on entity type\n const apiAttributesData: AttributeField[] = formData?.entityType\n ? attributes\n .filter((attr) => attr.scope?.includes(formData.entityType))\n .map((attr) => ({\n name: 'attrib.' + attr.name,\n data: attr.data,\n }))\n : []\n\n const readOnlyFieldsData: AttributeField[] = readOnlyFields.map((field) => ({\n name: field as string,\n readonly: true,\n data: {\n type: 'string',\n title: upperFirst(field as string),\n },\n }))\n\n // Combine custom fields with API attributes\n const allFieldsData = [...customFieldsData, ...apiAttributesData, ...readOnlyFieldsData]\n const sortToTop = ['path', 'name']\n const sortedFieldsData = [...allFieldsData].sort((a, b) => {\n const aIndex = sortToTop.indexOf(a.name)\n const bIndex = sortToTop.indexOf(b.name)\n if (aIndex === -1 && bIndex === -1) return 0\n if (aIndex === -1) return 1\n if (bIndex === -1) return -1\n return aIndex - bIndex\n })\n\n return sortedFieldsData\n }, [attributes, folderTypes, taskTypes, statuses, tags, formData?.entityType])\n\n //\n let enableEditing = false\n if (\n ['task', 'folder'].includes(formData?.entityType || '') &&\n !mixedFields.includes('projectName')\n ) {\n enableEditing = true\n }\n\n const entityType = formData?.entityType || 'task'\n const projectName = formData?.projectName || ''\n\n // Setup entity update functionality\n const { updateEntity } = useEntityUpdate({\n entities: entities.map((entity) => ({\n id: entity.id,\n projectName: entity.projectName || '',\n folderId: entity.folder?.id,\n users: entity.task?.assignees || [],\n })),\n entityType,\n })\n\n const handleChange: DetailsPanelAttributesEditorProps['onChange'] = (key, value) => {\n if (key.startsWith('attrib.')) {\n value = {\n [key.replace('attrib.', '')]: value,\n }\n key = 'attrib'\n }\n\n console.log('handleChange', key, value)\n\n // update the form data\n // @ts-ignore\n setFormData((prev) => ({\n ...prev,\n [key]: value,\n }))\n\n // update the entity in database\n updateEntity(key, value)\n }\n\n return (\n <Section style={{ padding: 8, overflow: 'hidden' }}>\n <DetailsPanelAttributesEditor\n fields={fields}\n form={formData || {}}\n mixedFields={mixedFields}\n isLoading={isLoading}\n enableEditing={enableEditing}\n onChange={handleChange}\n />\n </Section>\n )\n}\n"],"names":["_a","formData","jsx"],"mappings":"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"}
1
+ {"version":3,"file":"DetailsPanelAttributes.es.js","sources":["../../../../../src/components/DetailsPanelAttributes/DetailsPanelAttributes.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react'\nimport { Section } from '@ynput/ayon-react-components'\nimport { getMixedState } from '@shared/util'\nimport { useGetSiteInfoQuery, useGetProjectQuery } from '@shared/api'\nimport {\n DetailsPanelAttributesEditor,\n AttributeField,\n DetailsPanelAttributesEditorProps,\n} from './DetailsPanelAttributesEditor'\nimport { useEntityUpdate } from '@shared/hooks'\nimport { upperFirst } from 'lodash'\nimport type { DetailsPanelEntityData } from '@shared/api'\n\ntype EntityForm = {\n id: string\n name: string\n label: string | null | undefined\n entityType: 'folder' | 'task' | 'product' | 'version'\n taskType?: string\n folderType?: string\n productType?: string\n tags: string[]\n status: string\n updatedAt: string\n createdAt: string\n projectName: string\n path: string\n // attribs\n [key: string]: string | number | boolean | Date | any[] | Record<string, any> | undefined | null\n}\n\n// all fields in entity form are visible\nconst visibleFields: Array<keyof EntityForm> = [\n 'id',\n 'name',\n 'label',\n 'entityType',\n 'taskType',\n 'folderType',\n 'productType',\n 'tags',\n 'status',\n 'updatedAt',\n 'createdAt',\n 'projectName',\n 'path',\n]\n\nconst readOnlyFields: Array<keyof EntityForm> = [\n 'id',\n 'entityType',\n 'projectName',\n 'path',\n 'name',\n 'createdAt',\n 'updatedAt',\n]\n\nexport type DetailsPanelAttributesProps = {\n entities: DetailsPanelEntityData[]\n isLoading: boolean\n}\n\nexport const DetailsPanelAttributes = ({\n entities = [],\n isLoading,\n}: DetailsPanelAttributesProps) => {\n // form for project data\n const [mixedFields, setMixedFields] = useState<string[]>([])\n const [formData, setFormData] = useState<EntityForm | null>(null)\n\n const buildInitialForm = () => {\n // Group entity values by field name\n const valuesByField: Record<string, any[]> = {}\n // Track which fields have mixed values\n const mixedFieldsSet = new Set<string>()\n\n entities.forEach((entity) => {\n const mappedEntity: EntityForm = {\n id: entity.id,\n name: entity.name,\n label: entity.label,\n entityType: entity.entityType as 'folder' | 'task' | 'product' | 'version',\n createdAt: entity.createdAt,\n updatedAt: entity.updatedAt,\n projectName: entity.projectName,\n status: entity.status,\n tags: entity.tags || [],\n path: entity.folder?.path || '',\n folderType: entity.folder?.folderType,\n productType: entity.product?.productType,\n taskType: entity.task?.taskType,\n }\n\n // Process regular fields\n Object.keys(mappedEntity).forEach((key) => {\n if (visibleFields.includes(key as keyof EntityForm)) {\n valuesByField[key] = valuesByField[key] || []\n valuesByField[key].push((mappedEntity as any)[key])\n }\n })\n\n // Process attrib fields\n if (entity.attrib) {\n Object.keys(entity.attrib).forEach((key) => {\n const attribKey = `attrib.${key}`\n valuesByField[attribKey] = valuesByField[attribKey] || []\n valuesByField[attribKey].push(entity.attrib?.[key])\n })\n }\n })\n\n // Apply getMixedState to each field\n const formData = Object.entries(valuesByField).reduce((result, [key, values]) => {\n const { value, isMixed } = getMixedState(values)\n result[key] = value\n\n // Add to mixedFields if this field has mixed values\n if (isMixed) {\n mixedFieldsSet.add(key)\n }\n\n return result\n }, {} as Record<string, any>)\n\n setFormData(formData as EntityForm)\n // Update the mixedFields state with all fields that have mixed values\n setMixedFields(Array.from(mixedFieldsSet))\n }\n\n useEffect(() => {\n if (isLoading || entities.length === 0) return\n buildInitialForm()\n }, [entities, isLoading])\n\n const { data: projectData } = useGetProjectQuery(\n { projectName: formData?.projectName || '' },\n { skip: !formData?.projectName || mixedFields.includes('projectName') },\n )\n const { folderTypes = [], taskTypes = [], statuses = [], tags = [] } = projectData || {}\n\n const { data: info } = useGetSiteInfoQuery({ full: true })\n const { attributes = [] } = info || {}\n\n // build the fields array for defining the schema\n const fields: AttributeField[] = useMemo(() => {\n // Create custom fields as proper AttributeModel objects\n const customFieldsData: AttributeField[] = [\n {\n name: 'label',\n data: {\n type: 'string',\n title: 'Label',\n description: 'Used as a nice visual label only',\n },\n },\n {\n name: 'folderType',\n hidden: formData?.entityType !== 'folder',\n data: {\n type: 'string',\n title: 'Folder Type',\n description: 'Type of the folder',\n enum: folderTypes.map((type) => ({\n value: type.name,\n label: type.name,\n icon: type.icon,\n })),\n },\n },\n {\n name: 'taskType',\n hidden: formData?.entityType !== 'task',\n data: {\n type: 'string',\n title: 'Task Type',\n description: 'Type of the task',\n enum: taskTypes.map((type) => ({\n value: type.name,\n label: type.name,\n icon: type.icon,\n })),\n },\n },\n {\n name: 'status',\n data: {\n type: 'string',\n title: 'Status',\n description: 'The state the entity is in, is it approved or in progress etc.',\n enum: statuses.map((status) => ({\n value: status.name,\n label: status.name,\n icon: status.icon,\n color: status.color,\n })),\n },\n },\n {\n name: 'tags',\n data: {\n type: 'list_of_strings',\n title: 'Tags',\n enum: tags.map((tag) => ({\n value: tag.name,\n label: tag.name,\n color: tag.color,\n })),\n },\n },\n ]\n\n // Filter API attributes based on entity type\n const apiAttributesData: AttributeField[] = formData?.entityType\n ? attributes\n .filter((attr) => attr.scope?.includes(formData.entityType))\n .map((attr) => ({\n name: 'attrib.' + attr.name,\n data: attr.data,\n }))\n : []\n\n const readOnlyFieldsData: AttributeField[] = readOnlyFields.map((field) => ({\n name: field as string,\n readonly: true,\n data: {\n type: 'string',\n title: upperFirst(field as string),\n },\n }))\n\n // Combine custom fields with API attributes\n const allFieldsData = [...customFieldsData, ...apiAttributesData, ...readOnlyFieldsData]\n const sortToTop = ['path', 'name']\n const sortedFieldsData = [...allFieldsData].sort((a, b) => {\n const aIndex = sortToTop.indexOf(a.name)\n const bIndex = sortToTop.indexOf(b.name)\n if (aIndex === -1 && bIndex === -1) return 0\n if (aIndex === -1) return 1\n if (bIndex === -1) return -1\n return aIndex - bIndex\n })\n\n return sortedFieldsData\n }, [attributes, folderTypes, taskTypes, statuses, tags, formData?.entityType])\n\n //\n let enableEditing = false\n if (\n ['task', 'folder'].includes(formData?.entityType || '') &&\n !mixedFields.includes('projectName')\n ) {\n enableEditing = true\n }\n\n const entityType = formData?.entityType || 'task'\n const projectName = formData?.projectName || ''\n\n // Setup entity update functionality\n const { updateEntity } = useEntityUpdate({\n entities: entities.map((entity) => ({\n id: entity.id,\n projectName: entity.projectName || '',\n folderId: entity.folder?.id,\n users: entity.task?.assignees || [],\n })),\n entityType,\n })\n\n const handleChange: DetailsPanelAttributesEditorProps['onChange'] = (key, value) => {\n if (key.startsWith('attrib.')) {\n value = {\n [key.replace('attrib.', '')]: value,\n }\n key = 'attrib'\n }\n\n console.log('handleChange', key, value)\n\n // update the form data\n // @ts-ignore\n setFormData((prev) => ({\n ...prev,\n [key]: value,\n }))\n\n // update the entity in database\n updateEntity(key, value)\n }\n\n return (\n <Section style={{ padding: 8, overflow: 'hidden' }}>\n <DetailsPanelAttributesEditor\n fields={fields}\n form={formData || {}}\n mixedFields={mixedFields}\n isLoading={isLoading}\n enableEditing={enableEditing}\n onChange={handleChange}\n />\n </Section>\n )\n}\n"],"names":["_a","formData","jsx"],"mappings":"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"}
@@ -95,9 +95,12 @@ const Actions = ({
95
95
  entities,
96
96
  entityType,
97
97
  entitySubTypes,
98
- isLoadingEntity
98
+ isLoadingEntity,
99
+ searchParams,
100
+ onNavigate,
101
+ onSetSearchParams
99
102
  }) => {
100
- const { handleActionPayload } = useActionTriggers.useActionTriggers();
103
+ const { handleActionPayload } = useActionTriggers.useActionTriggers({ onNavigate, onSetSearchParams, searchParams });
101
104
  const [actionBeingConfigured, setActionBeingConfigured] = React.useState(null);
102
105
  const [interactiveForm, setInteractiveForm] = React.useState(null);
103
106
  const context = React.useMemo(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"Actions.cjs.js","sources":["../../../../../src/containers/Actions/Actions.tsx"],"sourcesContent":["import * as Styled from './Actions.styled'\nimport { MouseEvent, useState } from 'react'\nimport clsx from 'clsx'\nimport { toast } from 'react-toastify'\nimport { useMemo, useEffect } from 'react'\nimport { ActionContext, useExecuteActionMutation, useGetActionsFromContextQuery } from '@shared/api'\nimport { ActionsDropdown } from './ActionsDropdown'\nimport ActionIcon from './ActionIcon'\nimport { useActionTriggers } from '@shared/hooks'\nimport { ActionConfigDialog } from './ActionConfigDialog'\nimport { InteractiveActionDialog, InteractiveForm } from './InteractiveActionDialog'\n\nconst placeholder = {\n identifier: 'placeholder',\n label: 'Featured action slot',\n isPlaceholder: true,\n icon: { type: 'material-symbols', name: 'sync' },\n groupLabel: '',\n}\n\ntype ActionsProps = {\n entities: { id: string; projectName: string; entitySubType?: string }[]\n entityType: ActionContext['entityType']\n entitySubTypes?: string[]\n isLoadingEntity: boolean\n}\n\nexport const Actions = ({\n entities,\n entityType,\n entitySubTypes,\n isLoadingEntity,\n}: ActionsProps) => {\n // special triggers the actions can make to perform stuff on the client\n const { handleActionPayload } = useActionTriggers()\n const [actionBeingConfigured, setActionBeingConfigured] = useState<any>(null)\n const [interactiveForm, setInteractiveForm] = useState<any>(null)\n\n const context: ActionContext | null = useMemo(() => {\n if (!entities.length) return null\n if (!entities[0].projectName) return null\n\n // get a list of unique entity subtypes from loaded data\n const entitySubtypesLoaded = entities\n .filter((entity) => entity.entitySubType)\n .map((entity) => entity.entitySubType as string)\n .filter((value, index, self) => self.indexOf(value) === index && value)\n\n // try and use the passed in entitySubTypes, if not use the loaded ones\n const entitySubTypesToUse = entitySubTypes?.length ? entitySubTypes : entitySubtypesLoaded\n\n // all types except version/representation should have subtypes\n if (\n !entitySubTypesToUse?.length &&\n entityType !== 'version' &&\n entityType !== 'representation'\n ) {\n console.warn('No entity subtypes found')\n return null\n }\n\n return {\n projectName: entities[0].projectName,\n entityType: entityType,\n entityIds: entities.map((entity) => entity.id),\n entitySubtypes: entitySubTypesToUse,\n }\n }, [entities, entityType])\n\n useEffect(() => {\n setInteractiveForm(null)\n }, [context])\n\n const { data, isFetching: isFetchingActions } = useGetActionsFromContextQuery(\n { mode: 'simple', actionContext: context as ActionContext },\n { skip: !context },\n )\n\n const actions = data?.actions || []\n\n const categoryOrder = ['application', 'admin', 'workflow']\n // group actions by category\n // sort by hardcoded category, this will changing the future\n const groupedActions = useMemo(() => {\n // Step 1: Group actions by category\n const grouped = actions.reduce((acc: { [key: string]: any[] }, action) => {\n const category = action.category || 'uncategorized'\n if (!acc[category]) {\n acc[category] = []\n }\n acc[category].push(action)\n return acc\n }, {})\n\n // Step 5: Return the ordered groups\n return grouped\n }, [actions])\n\n // get categories that don't have a specific order (not in categoryOrder)\n // then sort them alphabetically\n const unorderedCategories = useMemo(\n () => [\n ...new Set(\n Object.keys(groupedActions)\n .filter((category) => !categoryOrder.includes(category))\n .sort((a, b) => a.localeCompare(b)),\n ),\n ],\n [groupedActions],\n )\n\n const categories = [...categoryOrder, ...unorderedCategories]\n\n // create the options for the dropdown, each category is separated by a divider and a title\n // for the divider we will use a custom dropdown item template\n const dropdownOptions = useMemo(() => {\n const options = []\n\n categories.forEach((category) => {\n if (!groupedActions[category] || !groupedActions[category].length) return\n\n options.push({\n label: category,\n header: true,\n value: category,\n disabled: true,\n })\n\n const groupOptions = groupedActions[category].map((action) => ({\n value: action.identifier,\n label: action.groupLabel ? action.groupLabel + ' ' + action.label : action.label,\n icon: action.icon,\n hasConfig: !!action.configFields,\n }))\n\n options.push(...groupOptions)\n })\n\n // if no actions, add placeholder\n if (!options.length) {\n options.push({\n label: 'No actions available',\n value: 'no-actions',\n disabled: true,\n header: true,\n })\n }\n\n return options\n }, [groupedActions, unorderedCategories, categoryOrder])\n\n const featuredNumber = 2\n\n const featuredActions = useMemo(() => {\n // Filter and sort to get initial featured actions\n let tempFeaturedActions = actions\n .filter((action) => action.featured)\n .sort((a, b) => (a.order || 0) - (b.order || 0))\n .slice(0, featuredNumber)\n\n // Check if we need to add more actions to reach featuredNumber\n if (tempFeaturedActions.length < featuredNumber) {\n categories.forEach((category) => {\n if (tempFeaturedActions.length >= featuredNumber) return\n const actions = groupedActions[category]\n if (!actions || !actions.length) return\n\n for (let i = tempFeaturedActions.length; i < featuredNumber; i++) {\n const action = actions[i]\n if (!action) break\n if (!action.icon) continue\n tempFeaturedActions.push(action)\n }\n })\n }\n\n return tempFeaturedActions\n }, [actions, groupedActions, placeholder])\n\n const [executeAction, { isLoading: isLoadingExecution, originalArgs }] =\n useExecuteActionMutation()\n const executingAction = isLoadingExecution && originalArgs?.identifier\n\n const handleExecuteAction = async (\n identifier: string,\n e?: MouseEvent<HTMLElement> | null,\n formData?: InteractiveForm,\n ) => {\n e?.preventDefault()\n const action = actions.find((option) => option.identifier === identifier)\n\n if (!action) {\n toast.error('Action not found')\n console.warn('Action not found', identifier)\n return\n }\n\n const params = {\n addonName: action.addonName as string,\n addonVersion: action.addonVersion as string,\n variant: action.variant,\n identifier: action.identifier,\n }\n\n const actionContext = { ...context }\n if (formData) {\n actionContext.formData = formData\n }\n\n let response = null\n\n try {\n response = await executeAction({ actionContext, ...params }).unwrap()\n } catch (error: any) {\n console.error('Error executing action', error)\n toast.error(error?.data?.detail || 'Error executing action')\n return\n }\n\n try {\n // Toast the message if it is available\n if (response?.message) {\n if (response?.success) {\n toast.success(response.message, { autoClose: 2000 })\n } else {\n toast.error(response.message, { autoClose: 2000 })\n }\n }\n\n // Even if response?.success is false, we still want to handle the payload\n // as it may contain useful information - complex error messages in form,\n // redirect to another page etc. If the action just needs to abort,\n // it raises exception instead of returning a response with success: false\n\n // Use the new hook to handle payload\n if (response?.payload) {\n if (response.type === 'form') {\n // action requests additional information from the user.\n // we show a dialog with the form and when the user submits it we call the action again\n\n // It probably does not make sense to move to the useActionTriggers hook\n // as it need contexts and the dialog\n const intf = {\n identifier,\n // @ts-expect-error\n title: response.payload['title'],\n // @ts-expect-error\n fields: response.payload['fields'],\n // @ts-expect-error\n submitLabel: response.payload['submit_label'],\n // @ts-expect-error\n cancelLabel: response.payload['cancel_label'],\n // @ts-expect-error\n submitIcon: response.payload['submit_icon'],\n // @ts-expect-error\n cancelIcon: response.payload['cancel_icon'],\n }\n setInteractiveForm(intf)\n } else {\n handleActionPayload(response.type as string, response.payload)\n }\n }\n } catch (error) {\n // got response, but failed to process it\n console.warn('Error during action response processing', error)\n toast.error('Error occured during action processing')\n }\n }\n\n const handleConfigureAction = (identifier: string) => {\n const action = actions.find((data) => data.identifier === identifier)\n if (!action) return\n setActionBeingConfigured(action)\n }\n\n const handleSubmitInteractiveForm = async (identifier: string, formData: InteractiveForm) => {\n handleExecuteAction(identifier, null, formData)\n }\n\n const loadingActions = [placeholder, placeholder, placeholder]\n\n const isLoading = isFetchingActions || isLoadingEntity\n const featuredActionsToDisplay = isLoading ? loadingActions : featuredActions\n\n return (\n <Styled.Actions className=\"actions\">\n {featuredActionsToDisplay.map((action, i) => (\n <Styled.FeaturedAction\n key={action.identifier + '-' + i}\n className={clsx('action', {\n loading: isLoading,\n // @ts-expect-error\n isPlaceholder: action.isPlaceholder,\n })}\n data-tooltip={action.groupLabel ? action.groupLabel + ' ' + action.label : action.label}\n // @ts-expect-error\n disabled={action.isPlaceholder}\n onClick={(e) => handleExecuteAction(action.identifier, e)}\n >\n {/* @ts-ignore */}\n <ActionIcon icon={action.icon} isExecuting={executingAction === action.identifier} />\n </Styled.FeaturedAction>\n ))}\n <ActionsDropdown\n options={dropdownOptions}\n isLoading={isLoading}\n onAction={handleExecuteAction}\n onConfig={handleConfigureAction}\n />\n <ActionConfigDialog\n action={actionBeingConfigured}\n // @ts-expect-error\n context={context}\n onClose={() => setActionBeingConfigured(null)}\n />\n <InteractiveActionDialog\n interactiveForm={interactiveForm}\n onClose={() => setInteractiveForm(null)}\n // @ts-expect-error\n onSubmit={handleSubmitInteractiveForm}\n />\n </Styled.Actions>\n )\n}\n"],"names":["useActionTriggers","useState","useMemo","useEffect","useGetActionsFromContextQuery","actions","useExecuteActionMutation","toast","data","jsxs","Styled.Actions","jsx","Styled.FeaturedAction","ActionsDropdown","ActionConfigDialog","InteractiveActionDialog"],"mappings":"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"}
1
+ {"version":3,"file":"Actions.cjs.js","sources":["../../../../../src/containers/Actions/Actions.tsx"],"sourcesContent":["import * as Styled from './Actions.styled'\nimport { MouseEvent, useState } from 'react'\nimport clsx from 'clsx'\nimport { toast } from 'react-toastify'\nimport { useMemo, useEffect } from 'react'\nimport { ActionContext, useExecuteActionMutation, useGetActionsFromContextQuery } from '@shared/api'\nimport { ActionsDropdown } from './ActionsDropdown'\nimport ActionIcon from './ActionIcon'\nimport { ActionTriggersProps, useActionTriggers } from '@shared/hooks'\nimport { ActionConfigDialog } from './ActionConfigDialog'\nimport { InteractiveActionDialog, InteractiveForm } from './InteractiveActionDialog'\n\nconst placeholder = {\n identifier: 'placeholder',\n label: 'Featured action slot',\n isPlaceholder: true,\n icon: { type: 'material-symbols', name: 'sync' },\n groupLabel: '',\n}\n\ninterface ActionsProps extends ActionTriggersProps {\n entities: { id: string; projectName: string; entitySubType?: string }[]\n entityType: ActionContext['entityType']\n entitySubTypes?: string[]\n isLoadingEntity: boolean\n}\n\nexport const Actions = ({\n entities,\n entityType,\n entitySubTypes,\n isLoadingEntity,\n searchParams,\n onNavigate,\n onSetSearchParams,\n}: ActionsProps) => {\n // special triggers the actions can make to perform stuff on the client\n const { handleActionPayload } = useActionTriggers({ onNavigate, onSetSearchParams, searchParams })\n const [actionBeingConfigured, setActionBeingConfigured] = useState<any>(null)\n const [interactiveForm, setInteractiveForm] = useState<any>(null)\n\n const context: ActionContext | null = useMemo(() => {\n if (!entities.length) return null\n if (!entities[0].projectName) return null\n\n // get a list of unique entity subtypes from loaded data\n const entitySubtypesLoaded = entities\n .filter((entity) => entity.entitySubType)\n .map((entity) => entity.entitySubType as string)\n .filter((value, index, self) => self.indexOf(value) === index && value)\n\n // try and use the passed in entitySubTypes, if not use the loaded ones\n const entitySubTypesToUse = entitySubTypes?.length ? entitySubTypes : entitySubtypesLoaded\n\n // all types except version/representation should have subtypes\n if (\n !entitySubTypesToUse?.length &&\n entityType !== 'version' &&\n entityType !== 'representation'\n ) {\n console.warn('No entity subtypes found')\n return null\n }\n\n return {\n projectName: entities[0].projectName,\n entityType: entityType,\n entityIds: entities.map((entity) => entity.id),\n entitySubtypes: entitySubTypesToUse,\n }\n }, [entities, entityType])\n\n useEffect(() => {\n setInteractiveForm(null)\n }, [context])\n\n const { data, isFetching: isFetchingActions } = useGetActionsFromContextQuery(\n { mode: 'simple', actionContext: context as ActionContext },\n { skip: !context },\n )\n\n const actions = data?.actions || []\n\n const categoryOrder = ['application', 'admin', 'workflow']\n // group actions by category\n // sort by hardcoded category, this will changing the future\n const groupedActions = useMemo(() => {\n // Step 1: Group actions by category\n const grouped = actions.reduce((acc: { [key: string]: any[] }, action) => {\n const category = action.category || 'uncategorized'\n if (!acc[category]) {\n acc[category] = []\n }\n acc[category].push(action)\n return acc\n }, {})\n\n // Step 5: Return the ordered groups\n return grouped\n }, [actions])\n\n // get categories that don't have a specific order (not in categoryOrder)\n // then sort them alphabetically\n const unorderedCategories = useMemo(\n () => [\n ...new Set(\n Object.keys(groupedActions)\n .filter((category) => !categoryOrder.includes(category))\n .sort((a, b) => a.localeCompare(b)),\n ),\n ],\n [groupedActions],\n )\n\n const categories = [...categoryOrder, ...unorderedCategories]\n\n // create the options for the dropdown, each category is separated by a divider and a title\n // for the divider we will use a custom dropdown item template\n const dropdownOptions = useMemo(() => {\n const options = []\n\n categories.forEach((category) => {\n if (!groupedActions[category] || !groupedActions[category].length) return\n\n options.push({\n label: category,\n header: true,\n value: category,\n disabled: true,\n })\n\n const groupOptions = groupedActions[category].map((action) => ({\n value: action.identifier,\n label: action.groupLabel ? action.groupLabel + ' ' + action.label : action.label,\n icon: action.icon,\n hasConfig: !!action.configFields,\n }))\n\n options.push(...groupOptions)\n })\n\n // if no actions, add placeholder\n if (!options.length) {\n options.push({\n label: 'No actions available',\n value: 'no-actions',\n disabled: true,\n header: true,\n })\n }\n\n return options\n }, [groupedActions, unorderedCategories, categoryOrder])\n\n const featuredNumber = 2\n\n const featuredActions = useMemo(() => {\n // Filter and sort to get initial featured actions\n let tempFeaturedActions = actions\n .filter((action) => action.featured)\n .sort((a, b) => (a.order || 0) - (b.order || 0))\n .slice(0, featuredNumber)\n\n // Check if we need to add more actions to reach featuredNumber\n if (tempFeaturedActions.length < featuredNumber) {\n categories.forEach((category) => {\n if (tempFeaturedActions.length >= featuredNumber) return\n const actions = groupedActions[category]\n if (!actions || !actions.length) return\n\n for (let i = tempFeaturedActions.length; i < featuredNumber; i++) {\n const action = actions[i]\n if (!action) break\n if (!action.icon) continue\n tempFeaturedActions.push(action)\n }\n })\n }\n\n return tempFeaturedActions\n }, [actions, groupedActions, placeholder])\n\n const [executeAction, { isLoading: isLoadingExecution, originalArgs }] =\n useExecuteActionMutation()\n const executingAction = isLoadingExecution && originalArgs?.identifier\n\n const handleExecuteAction = async (\n identifier: string,\n e?: MouseEvent<HTMLElement> | null,\n formData?: InteractiveForm,\n ) => {\n e?.preventDefault()\n const action = actions.find((option) => option.identifier === identifier)\n\n if (!action) {\n toast.error('Action not found')\n console.warn('Action not found', identifier)\n return\n }\n\n const params = {\n addonName: action.addonName as string,\n addonVersion: action.addonVersion as string,\n variant: action.variant,\n identifier: action.identifier,\n }\n\n const actionContext = { ...context }\n if (formData) {\n actionContext.formData = formData\n }\n\n let response = null\n\n try {\n response = await executeAction({ actionContext, ...params }).unwrap()\n } catch (error: any) {\n console.error('Error executing action', error)\n toast.error(error?.data?.detail || 'Error executing action')\n return\n }\n\n try {\n // Toast the message if it is available\n if (response?.message) {\n if (response?.success) {\n toast.success(response.message, { autoClose: 2000 })\n } else {\n toast.error(response.message, { autoClose: 2000 })\n }\n }\n\n // Even if response?.success is false, we still want to handle the payload\n // as it may contain useful information - complex error messages in form,\n // redirect to another page etc. If the action just needs to abort,\n // it raises exception instead of returning a response with success: false\n\n // Use the new hook to handle payload\n if (response?.payload) {\n if (response.type === 'form') {\n // action requests additional information from the user.\n // we show a dialog with the form and when the user submits it we call the action again\n\n // It probably does not make sense to move to the useActionTriggers hook\n // as it need contexts and the dialog\n const intf = {\n identifier,\n // @ts-expect-error\n title: response.payload['title'],\n // @ts-expect-error\n fields: response.payload['fields'],\n // @ts-expect-error\n submitLabel: response.payload['submit_label'],\n // @ts-expect-error\n cancelLabel: response.payload['cancel_label'],\n // @ts-expect-error\n submitIcon: response.payload['submit_icon'],\n // @ts-expect-error\n cancelIcon: response.payload['cancel_icon'],\n }\n setInteractiveForm(intf)\n } else {\n handleActionPayload(response.type as string, response.payload)\n }\n }\n } catch (error) {\n // got response, but failed to process it\n console.warn('Error during action response processing', error)\n toast.error('Error occured during action processing')\n }\n }\n\n const handleConfigureAction = (identifier: string) => {\n const action = actions.find((data) => data.identifier === identifier)\n if (!action) return\n setActionBeingConfigured(action)\n }\n\n const handleSubmitInteractiveForm = async (identifier: string, formData: InteractiveForm) => {\n handleExecuteAction(identifier, null, formData)\n }\n\n const loadingActions = [placeholder, placeholder, placeholder]\n\n const isLoading = isFetchingActions || isLoadingEntity\n const featuredActionsToDisplay = isLoading ? loadingActions : featuredActions\n\n return (\n <Styled.Actions className=\"actions\">\n {featuredActionsToDisplay.map((action, i) => (\n <Styled.FeaturedAction\n key={action.identifier + '-' + i}\n className={clsx('action', {\n loading: isLoading,\n // @ts-expect-error\n isPlaceholder: action.isPlaceholder,\n })}\n data-tooltip={action.groupLabel ? action.groupLabel + ' ' + action.label : action.label}\n // @ts-expect-error\n disabled={action.isPlaceholder}\n onClick={(e) => handleExecuteAction(action.identifier, e)}\n >\n {/* @ts-ignore */}\n <ActionIcon icon={action.icon} isExecuting={executingAction === action.identifier} />\n </Styled.FeaturedAction>\n ))}\n <ActionsDropdown\n options={dropdownOptions}\n isLoading={isLoading}\n onAction={handleExecuteAction}\n onConfig={handleConfigureAction}\n />\n <ActionConfigDialog\n action={actionBeingConfigured}\n // @ts-expect-error\n context={context}\n onClose={() => setActionBeingConfigured(null)}\n />\n <InteractiveActionDialog\n interactiveForm={interactiveForm}\n onClose={() => setInteractiveForm(null)}\n // @ts-expect-error\n onSubmit={handleSubmitInteractiveForm}\n />\n </Styled.Actions>\n )\n}\n"],"names":["useActionTriggers","useState","useMemo","useEffect","useGetActionsFromContextQuery","actions","useExecuteActionMutation","toast","data","jsxs","Styled.Actions","jsx","Styled.FeaturedAction","ActionsDropdown","ActionConfigDialog","InteractiveActionDialog"],"mappings":"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"}
@@ -93,9 +93,12 @@ const Actions = ({
93
93
  entities,
94
94
  entityType,
95
95
  entitySubTypes,
96
- isLoadingEntity
96
+ isLoadingEntity,
97
+ searchParams,
98
+ onNavigate,
99
+ onSetSearchParams
97
100
  }) => {
98
- const { handleActionPayload } = useActionTriggers();
101
+ const { handleActionPayload } = useActionTriggers({ onNavigate, onSetSearchParams, searchParams });
99
102
  const [actionBeingConfigured, setActionBeingConfigured] = useState(null);
100
103
  const [interactiveForm, setInteractiveForm] = useState(null);
101
104
  const context = useMemo(() => {