@visns-studio/visns-components 5.24.0 → 5.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -94,7 +94,7 @@
94
94
  "react-dom": "^17.0.0 || ^18.0.0"
95
95
  },
96
96
  "name": "@visns-studio/visns-components",
97
- "version": "5.24.0",
97
+ "version": "5.25.0",
98
98
  "description": "Various packages to assist in the development of our Custom Applications.",
99
99
  "main": "src/index.js",
100
100
  "files": [
@@ -249,23 +249,37 @@ function MultiSelect({
249
249
 
250
250
  const handleCreate = async (inputValue) => {
251
251
  if (creatableConfig.url && creatableConfig.method) {
252
+ // creatableConfig.payload lets the consumer send extra fields
253
+ // with the create (e.g. {is_active: 1}); creatableConfig.field
254
+ // overrides the body key for the typed value (default "label",
255
+ // e.g. "name" for entities whose label column is `name`).
256
+ const valueKey = creatableConfig.field || 'label';
252
257
  const res = await CustomFetch(
253
258
  creatableConfig.url,
254
259
  creatableConfig.method,
255
- { label: inputValue }
260
+ { [valueKey]: inputValue, ...(creatableConfig.payload || {}) }
256
261
  );
257
262
 
258
- if (res && res.data) {
263
+ const created = res?.data?.data ?? res?.data;
264
+ if (res && created && created.id) {
259
265
  const newOption = {
260
- value: res.data.id,
261
- label: res.data.label || inputValue,
262
- // Make sure description is preserved if it exists in the response
263
- description: res.data.description || null,
264
- ...res.data,
266
+ value: created.id,
267
+ label: created.label || created.name || inputValue,
268
+ description: created.description || null,
269
+ ...created,
265
270
  };
266
271
 
272
+ const nextValue = [...selectValue, newOption];
267
273
  setSelectOptions((prevOptions) => [...prevOptions, newOption]);
268
- setSelectValue((prevValues) => [...prevValues, newOption]);
274
+ setSelectValue(nextValue);
275
+ // Propagate to the parent form so the freshly-created option
276
+ // is part of the field value and is persisted on save —
277
+ // updating local state alone left it out of the submission.
278
+ onChange(
279
+ nextValue,
280
+ { action: 'create-option', option: newOption },
281
+ settings.id
282
+ );
269
283
  }
270
284
  }
271
285
  };
@@ -731,9 +731,66 @@ export const renderMultiDropdownColumn = ({
731
731
  ).toLowerCase()
732
732
  );
733
733
 
734
+ const save = (ids) => {
735
+ if (
736
+ column.update?.key &&
737
+ column.update?.url &&
738
+ column.update?.method
739
+ ) {
740
+ handleDropdownUpdate(
741
+ column.update.key,
742
+ `${column.update.url}/${data.id}`,
743
+ column.update.method,
744
+ ids
745
+ );
746
+ }
747
+ };
748
+
749
+ // creatable: typing a value not in the list offers "Add …"
750
+ // which POSTs a new entry to the source entity and adds it to
751
+ // this row's selection.
752
+ const handleCreate = async (inputValue) => {
753
+ const label = String(inputValue ?? '').trim();
754
+ if (
755
+ !label ||
756
+ !column.creatable?.url ||
757
+ !column.creatable?.field
758
+ ) {
759
+ return;
760
+ }
761
+ try {
762
+ const res = await CustomFetch(
763
+ column.creatable.url,
764
+ 'POST',
765
+ {
766
+ [column.creatable.field]: label,
767
+ ...(column.creatable.payload || {}),
768
+ }
769
+ );
770
+ const created = res?.data?.data;
771
+ if (res?.data?.error === '' && created?.id) {
772
+ save([
773
+ ...value.map((v) => v.value),
774
+ created.id,
775
+ ]);
776
+ } else {
777
+ toast.error(
778
+ res?.data?.error || 'Could not create entry'
779
+ );
780
+ }
781
+ } catch (err) {
782
+ console.error(err);
783
+ toast.error('Could not create entry');
784
+ }
785
+ };
786
+
787
+ const SelectComponent = column.creatable
788
+ ? CreatableSelect
789
+ : Select;
790
+
734
791
  return (
735
792
  <div>
736
- <Select
793
+ <SelectComponent
737
794
  isMulti
738
795
  options={options}
739
796
  value={value}
@@ -746,20 +803,13 @@ export const renderMultiDropdownColumn = ({
746
803
  }
747
804
  menuPlacement="auto"
748
805
  styles={compactSelectStyles}
749
- onChange={(selected) => {
750
- if (
751
- column.update?.key &&
752
- column.update?.url &&
753
- column.update?.method
754
- ) {
755
- handleDropdownUpdate(
756
- column.update.key,
757
- `${column.update.url}/${data.id}`,
758
- column.update.method,
759
- (selected || []).map((s) => s.value)
760
- );
761
- }
762
- }}
806
+ {...(column.creatable && {
807
+ onCreateOption: handleCreate,
808
+ formatCreateLabel: (v) => `Add "${v}"`,
809
+ })}
810
+ onChange={(selected) =>
811
+ save((selected || []).map((s) => s.value))
812
+ }
763
813
  />
764
814
  {otherSelected && (
765
815
  <OtherValueInput