@verifiedinc-public/shared-ui-elements 12.18.0 → 12.19.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/dist/components/NetworkRules/editor/NetworkRuleEditorForm.d.ts +5 -1
- package/dist/components/NetworkRules/editor/NetworkRuleEditorForm.mjs +1 -1
- package/dist/components/NetworkRules/editor/useNetworkRulePresetManager.d.ts +11 -5
- package/dist/components/NetworkRules/editor/useNetworkRulePresetManager.mjs +1 -1
- package/dist/components/NetworkRules/types.d.ts +7 -3
- package/package.json +1 -1
|
@@ -4,7 +4,11 @@ export interface NetworkRuleEditorFormProps {
|
|
|
4
4
|
rule?: Partial<NetworkRule> | null;
|
|
5
5
|
/** Pre-filled, editable name for a new rule, e.g. "Rule #7". */
|
|
6
6
|
defaultName?: string;
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Offer "Add … as a preset" for a typed note or free-text value. With `services.updatePresets`
|
|
9
|
+
* the preset is stored as soon as it is picked; otherwise, or when that write is refused, the
|
|
10
|
+
* grown lists arrive with `onSubmit`.
|
|
11
|
+
*/
|
|
8
12
|
canCreatePresets?: boolean;
|
|
9
13
|
/**
|
|
10
14
|
* Offer edit and delete controls on saved presets. Unlike a new preset, a change is written
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{useState as
|
|
1
|
+
"use strict";import{useState as M,useMemo as z,useEffect as x,useRef as ee}from"react";import{Stack as p,Alert as A,Button as k,TextField as re,FormControlLabel as te,Switch as oe,CircularProgress as ae,Skeleton as P}from"@mui/material";import{zodResolver as ne}from"@hookform/resolvers/zod";import{addDays as se}from"date-fns";import{useForm as ie,useWatch as le,FormProvider as de,Controller as D}from"react-hook-form";import{useNetworkRuleStatuses as ue}from"../NetworkRules.context.mjs";import{NetworkRulePresetDeleteDialog as me}from"../dialog/NetworkRulePresetDeleteDialog.mjs";import{NetworkRulePresetEditDialog as ce}from"../dialog/NetworkRulePresetEditDialog.mjs";import{useNetworkRulesCatalog as fe}from"../hooks/useNetworkRulesCatalog.mjs";import{getPresets as L}from"../utils/catalog.mjs";import{fromNetworkRuleFormValues as he,toNetworkRuleFormValues as pe,emptyConditionFormValues as ge}from"../utils/condition.mjs";import{ruleDateToDay as ve}from"../utils/date.mjs";import{ConditionsField as be}from"./fields/ConditionsField.mjs";import{DateField as W}from"./fields/DateField.mjs";import{MetadataField as Ce}from"./fields/MetadataField.mjs";import{NotesField as Pe}from"./fields/NotesField.mjs";import{StatusField as De}from"./fields/StatusField.mjs";import{createNetworkRuleFormSchema as ye}from"./schema.mjs";import{useNetworkRulePresetManager as Se}from"./useNetworkRulePresetManager.mjs";import{jsxs as c,jsx as e,Fragment as Ee}from"react/jsx-runtime";function I(s,u){const i=pe(s);return!(s!=null&&s.uuid)&&!i.name&&u&&(i.name=u),i.conditions.length===0&&(i.conditions=[ge()]),i}function xe(s,u){const i=[],v=[];for(const f of u){const g=f.message??f.code;if(typeof f.index=="number"){const h=`conditions.${f.index}.values`;s.setError(h,{type:"server",message:g}),v.push(h)}else i.push(g)}return i.length>0&&s.setError("root.server",{type:"server",message:i.join(" ")}),v}function ke(){return c(p,{spacing:2,"aria-busy":!0,children:[e(P,{variant:"rounded",height:56}),e(P,{variant:"rounded",height:56}),e(P,{variant:"rounded",height:56}),e(P,{variant:"rounded",height:120})]})}function Fe({rule:s,defaultName:u,canCreatePresets:i=!1,canManagePresets:v=i,onSubmit:f,onCancel:g,isSubmitting:h=!1,serverErrors:F,focusConditionIndex:$,submitLabel:O="Save",cancelLabel:U="Cancel",disabled:q=!1,id:G,hideActions:H=!1,onDirtyChange:y}){var R,w;const b=fe(),o=b.data,J=ue()??o?.statuses??[],C=s?.uuid??"new",[N,K]=M(C),X=z(()=>ne(ye(o?.metadata)),[o]),t=ie({defaultValues:I(s,u),resolver:X,mode:"onSubmit",reValidateMode:"onChange"}),[m,T]=M({}),l=Se({form:t,catalog:o,addedPresets:m,setAddedPresets:T,canCreate:i,enabled:v});x(()=>{C!==N&&(t.reset(I(s,u)),T({}),K(C))},[t,s,u,C,N]);const S=ee([]);x(()=>{t.clearErrors("root.server"),S.current.length>0&&t.clearErrors(S.current),S.current=xe(t,F??[])},[t,F]);const{isDirty:B}=t.formState;x(()=>{y?.(B)},[B,y]);const d=q||h,j=(w=(R=t.formState.errors.root)==null?void 0:R.server)==null?void 0:w.message,Y=le({control:t.control,name:"startDate"}),V=ve(Y),Z=V?se(V,1):void 0,Q=z(()=>L(o,"notes",m),[o,m]),E=r=>r?a=>{r("notes",a)}:void 0,_=t.handleSubmit(async r=>{if(o===void 0)return;const a=Object.fromEntries(Object.keys(m).map(n=>[n,L(o,n,m)]));await f(he(r),{presets:a})});return c(de,{...t,children:[e("form",{id:G,noValidate:!0,onSubmit:r=>{r.preventDefault(),r.stopPropagation(),_(r)},children:c(p,{spacing:3,children:[b.isError&&e(A,{severity:"error",action:e(k,{color:"inherit",size:"small",onClick:()=>{b.refetch()},children:"Retry"}),children:"The rule catalog could not be loaded."}),j&&e(A,{severity:"error",children:j}),o===void 0?!b.isError&&e(ke,{}):c(Ee,{children:[c(p,{direction:{xs:"column",md:"row"},spacing:2,children:[e(D,{control:t.control,name:"name",render:({field:r,fieldState:a})=>{var n;return e(re,{...r,label:"Rule Name",required:!0,fullWidth:!0,disabled:d,error:a.error!==void 0,helperText:(n=a.error)==null?void 0:n.message,sx:{flex:2}})}}),e(p,{flex:1,minWidth:{md:220},children:e(De,{statuses:J,disabled:d})})]}),c(p,{direction:{xs:"column",md:"row"},spacing:2,children:[e(D,{control:t.control,name:"startDate",render:({field:r,fieldState:a})=>{var n;return e(W,{label:"Starts",value:r.value,onChange:r.onChange,onBlur:r.onBlur,disabled:d,error:a.error!==void 0,helperText:((n=a.error)==null?void 0:n.message)??"Starts applying at the start of this day (UTC)"})}}),e(D,{control:t.control,name:"endDate",render:({field:r,fieldState:a})=>{var n;return e(W,{label:"Ends",value:r.value,onChange:r.onChange,onBlur:r.onBlur,disabled:d,minDate:Z,error:a.error!==void 0,helperText:((n=a.error)==null?void 0:n.message)??"Stops applying at the start of this day (UTC)"})}})]}),e(Pe,{presets:Q,onCreatePreset:E(l.onCreatePreset),onEditPreset:E(l.onEditPreset),onDeletePreset:E(l.onDeletePreset),disabled:d}),e(D,{control:t.control,name:"enabled",render:({field:r})=>e(te,{label:"Enabled",control:e(oe,{checked:r.value,onChange:(a,n)=>{r.onChange(n)},onBlur:r.onBlur,inputRef:r.ref,disabled:d})})}),e(be,{catalog:o,addedPresets:m,onCreatePreset:l.onCreatePreset,onEditPreset:l.onEditPreset,onDeletePreset:l.onDeletePreset,disabled:d,focusIndex:$}),e(Ce,{catalog:o,addedPresets:m,onCreatePreset:l.onCreatePreset,onEditPreset:l.onEditPreset,onDeletePreset:l.onDeletePreset,disabled:d})]}),!H&&c(p,{direction:"row",spacing:1,justifyContent:"flex-end",children:[g&&e(k,{onClick:g,disabled:h,children:U}),e(k,{type:"submit",variant:"contained",disabled:d||o===void 0,startIcon:h?e(ae,{size:16,color:"inherit"}):void 0,children:O})]})]})}),e(ce,{...l.editDialogProps}),e(me,{...l.deleteDialogProps})]})}export{Fe as NetworkRuleEditorForm};
|
|
@@ -7,21 +7,27 @@ export interface UseNetworkRulePresetManagerOptions {
|
|
|
7
7
|
/** The rule being edited, which follows a rename when the user asks rules to. */
|
|
8
8
|
form: UseFormReturn<NetworkRuleFormValues>;
|
|
9
9
|
catalog: NetworkRuleCatalog | undefined;
|
|
10
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* Presets not stored yet: created without `services.updatePresets`, or refused by it. They go
|
|
12
|
+
* out with the rule on submit, and are changed in place here, without a write.
|
|
13
|
+
*/
|
|
11
14
|
addedPresets: NetworkRulePresets;
|
|
12
15
|
setAddedPresets: Dispatch<SetStateAction<NetworkRulePresets>>;
|
|
16
|
+
/** Offer "Add … as a preset". Without `services.updatePresets` the preset waits for the submit. */
|
|
17
|
+
canCreate: boolean;
|
|
13
18
|
/** Off, or on without `services.updatePresets`, gives no handlers, so the inputs show no controls. */
|
|
14
19
|
enabled: boolean;
|
|
15
20
|
}
|
|
16
21
|
export interface NetworkRulePresetManager {
|
|
22
|
+
onCreatePreset?: (field: string, value: string) => void;
|
|
17
23
|
onEditPreset?: (field: string, value: string) => void;
|
|
18
24
|
onDeletePreset?: (field: string, value: string) => void;
|
|
19
25
|
editDialogProps: NetworkRulePresetEditDialogProps;
|
|
20
26
|
deleteDialogProps: NetworkRulePresetDeleteDialogProps;
|
|
21
27
|
}
|
|
22
28
|
/**
|
|
23
|
-
* The edit and delete flows behind the preset dropdowns: which preset a dialog is about,
|
|
24
|
-
* of the field's revised list, and the error the dialog shows when that write fails. A
|
|
25
|
-
* touches rules only when the user ticks the box for it, and then also the rule open here.
|
|
29
|
+
* The create, edit and delete flows behind the preset dropdowns: which preset a dialog is about,
|
|
30
|
+
* the write of the field's revised list, and the error the dialog shows when that write fails. A
|
|
31
|
+
* rename touches rules only when the user ticks the box for it, and then also the rule open here.
|
|
26
32
|
*/
|
|
27
|
-
export declare function useNetworkRulePresetManager({ form, catalog, addedPresets, setAddedPresets, enabled, }: UseNetworkRulePresetManagerOptions): NetworkRulePresetManager;
|
|
33
|
+
export declare function useNetworkRulePresetManager({ form, catalog, addedPresets, setAddedPresets, canCreate, enabled, }: UseNetworkRulePresetManagerOptions): NetworkRulePresetManager;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{useState as
|
|
1
|
+
"use strict";import{useState as R,useRef as L}from"react";import{useQueryClient as F}from"@tanstack/react-query";import{useNetworkRulesServices as I}from"../NetworkRules.context.mjs";import{networkRulesCatalogQueryKey as W}from"../hooks/useNetworkRulesCatalog.mjs";import{useRenameNetworkRulePreset as q}from"../hooks/useRenameNetworkRulePreset.mjs";import{useUpdateNetworkRulesPresets as b}from"../hooks/useUpdateNetworkRulesPresets.mjs";import{getSavedPresets as N,getPresetMaxLength as z,getPresets as B}from"../utils/catalog.mjs";import{dedupeValues as G}from"../utils/condition.mjs";import{NETWORK_RULE_METADATA_PRESET_FIELDS as T}from"../utils/metadata.mjs";import{removePreset as _,replacePreset as x}from"../utils/presets.mjs";const H="The presets could not be saved.";function J(a,r,i,n){const d={shouldDirty:!0,shouldValidate:a.formState.isSubmitted},m=a.getValues();if(r==="notes"){m.notes===i&&a.setValue("notes",n,d);return}if(r===T.key){m.metadata.forEach((u,c)=>{u.key===i&&a.setValue(`metadata.${c}.key`,n,d)});return}if(r===T.value){m.metadata.forEach((u,c)=>{u.type==="string"&&u.value===i&&a.setValue(`metadata.${c}.value`,n,d)});return}m.conditions.forEach((u,c)=>{u.key===r&&u.values.includes(i)&&a.setValue(`conditions.${c}.values`,G(u.values.map(P=>P===i?n:P)),d)})}function M(a,r,i){const n=_(a[r]??[],i);return n.length>0?{...a,[r]:n}:Object.fromEntries(Object.entries(a).filter(([d])=>d!==r))}function X({form:a,catalog:r,addedPresets:i,setAddedPresets:n,canCreate:d,enabled:m}){const{scope:u,updatePresets:c,renamePreset:P}=I(),O=F(),Q=b(),f=q(),v=b(),[o,w]=R(null),[p,E]=R(null),[k,S]=R(!1),D=m&&c!==void 0,y=e=>N(O.getQueryData(W(u))??r,e),U=(e,t)=>{n(s=>{const l=s[e]??[];return l.includes(t)?s:{...s,[e]:[...l,t]}})},h=L(Promise.resolve()),$=(e,t)=>{U(e,t),c!==void 0&&(h.current=h.current.then(async()=>{const s=y(e);s.includes(t)||await Q.mutateAsync({[e]:[...s,t]}),n(l=>M(l,e,t))}).catch(()=>{}))},A=async(e,t)=>{S(!0);try{await h.current}finally{S(!1)}return y(e).includes(t)},g=()=>{w(null),E(null),f.reset(),v.reset()},C=e=>e?e.message||H:void 0,j={open:o!==null,value:o?.value,presets:o?B(r,o.field,i):void 0,maxLength:o?z(r,o.field):void 0,canUpdateRules:o!==null&&P!==void 0&&N(r,o.field).includes(o.value),isSaving:f.isPending||k,error:C(f.error),onClose:g,onSave:async(e,{updateRules:t})=>{if(!o)return;const{field:s,value:l}=o;if(await A(s,l)){try{await f.mutateAsync({field:s,from:l,to:e,presets:x(y(s),l,e),updateRules:t})}catch{return}t&&J(a,s,l,e)}else n(V=>({...V,[s]:x(V[s]??[],l,e)}));g()}},K={open:p!==null,value:p?.value,isDeleting:v.isPending||k,error:C(v.error),onClose:g,onConfirm:async()=>{if(!p)return;const{field:e,value:t}=p;if(await A(e,t))try{await v.mutateAsync({[e]:_(y(e),t)})}catch{return}else n(s=>M(s,e,t));g()}};return{onCreatePreset:d?$:void 0,onEditPreset:D?(e,t)=>{f.reset(),w({field:e,value:t})}:void 0,onDeletePreset:D?(e,t)=>{v.reset(),E({field:e,value:t})}:void 0,editDialogProps:j,deleteDialogProps:K}}export{X as useNetworkRulePresetManager};
|
|
@@ -129,7 +129,10 @@ export type NetworkRulePresetRename = {
|
|
|
129
129
|
};
|
|
130
130
|
/** Handed back with the submitted rule. */
|
|
131
131
|
export type NetworkRuleSubmitExtras = {
|
|
132
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* Preset lists that grew while editing and are not stored yet, ready to store: the host had no
|
|
134
|
+
* `updatePresets`, or refused the write when the preset was picked. Empty otherwise.
|
|
135
|
+
*/
|
|
133
136
|
presets: NetworkRulePresets;
|
|
134
137
|
};
|
|
135
138
|
/** A server validation error; `index` points at the offending condition. */
|
|
@@ -157,8 +160,9 @@ export type NetworkRulesServices = {
|
|
|
157
160
|
scope?: string;
|
|
158
161
|
getCatalog: (signal?: AbortSignal) => Promise<NetworkRuleCatalog>;
|
|
159
162
|
/**
|
|
160
|
-
* Stores the given fields' complete lists, leaving the others alone. With it
|
|
161
|
-
*
|
|
163
|
+
* Stores the given fields' complete lists, leaving the others alone. With it a new preset is
|
|
164
|
+
* stored as soon as it is picked, saved presets get edit and delete controls, and the catalog
|
|
165
|
+
* is refetched after each write.
|
|
162
166
|
*/
|
|
163
167
|
updatePresets?: (presets: NetworkRulePresets) => Promise<void>;
|
|
164
168
|
/**
|