feat: ui json formatting

This commit is contained in:
Julian Sommer
2026-06-17 16:02:48 +10:00
parent eced3f733a
commit d88783d5bf
15 changed files with 192 additions and 35 deletions
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./back-link.js";import{t as i}from"./cli-command-panel.js";import{o as a}from"./iam.js";import{Ir as o,Rn as s}from"./index.js";import{a as ee,i as te,t as ne}from"./zod.js";import{t as re}from"./form-actions.js";import{t as ie}from"./page-heading.js";import{a as c,r as l,t as u}from"./field.js";import{t as d}from"./textarea.js";import{r as f}from"./iam2.js";var ae=t(),p=e(),oe=JSON.stringify({Version:`2012-10-17`,Statement:[{Effect:`Allow`,Action:`*`,Resource:`*`}]},null,2);function m(){let e=(0,ae.c)(67),t=o(),m=a(),h;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(h={resolver:ne(f),defaultValues:{policyDocument:oe}},e[0]=h):h=e[0];let{handleSubmit:g,register:_,control:v,formState:ce}=te(h),{errors:y,isSubmitting:b}=ce,x;e[1]===v?x=e[2]:(x={control:v},e[1]=v,e[2]=x);let S=ee(x),C;e[3]===S?C=e[4]:(C=e=>e?S[e]:void 0,e[3]=S,e[4]=C);let w=C,T;e[5]!==m||e[6]!==t?(T=async e=>{await m.mutateAsync(e),t({to:`/iam/list-policies`})},e[5]=m,e[6]=t,e[7]=T):T=e[7];let E=T,D,O;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(D=(0,p.jsx)(r,{to:`/iam/list-policies`,children:`Back to policies`}),O=(0,p.jsx)(ie,{title:`Create Policy`}),e[8]=D,e[9]=O):(D=e[8],O=e[9]);let k;e[10]===m.error?k=e[11]:(k=m.error&&(0,p.jsx)(n,{error:m.error,msg:`Failed to create policy`}),e[10]=m.error,e[11]=k);let A;e[12]!==g||e[13]!==E?(A=g(E),e[12]=g,e[13]=E,e[14]=A):A=e[14];let j;e[15]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,p.jsx)(c,{children:(0,p.jsx)(`label`,{htmlFor:`policyName`,children:`Policy Name`})}),e[15]=j):j=e[15];let M=!!y.policyName,N;e[16]===_?N=e[17]:(N=_(`policyName`),e[16]=_,e[17]=N);let P;e[18]!==M||e[19]!==N?(P=(0,p.jsx)(s,{"aria-invalid":M,id:`policyName`,placeholder:`my-policy...`,...N}),e[18]=M,e[19]=N,e[20]=P):P=e[20];let F;e[21]===y.policyName?F=e[22]:(F=(0,p.jsx)(l,{errors:[y.policyName]}),e[21]=y.policyName,e[22]=F);let I;e[23]!==P||e[24]!==F?(I=(0,p.jsxs)(u,{children:[j,P,F]}),e[23]=P,e[24]=F,e[25]=I):I=e[25];let L;e[26]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,p.jsx)(c,{children:(0,p.jsx)(`label`,{htmlFor:`description`,children:`Description (optional)`})}),e[26]=L):L=e[26];let R;e[27]===_?R=e[28]:(R=_(`description`),e[27]=_,e[28]=R);let z;e[29]===R?z=e[30]:(z=(0,p.jsx)(s,{id:`description`,placeholder:`A brief description of this policy...`,...R}),e[29]=R,e[30]=z);let B;e[31]===y.description?B=e[32]:(B=(0,p.jsx)(l,{errors:[y.description]}),e[31]=y.description,e[32]=B);let V;e[33]!==z||e[34]!==B?(V=(0,p.jsxs)(u,{children:[L,z,B]}),e[33]=z,e[34]=B,e[35]=V):V=e[35];let H;e[36]===Symbol.for(`react.memo_cache_sentinel`)?(H=(0,p.jsx)(c,{children:(0,p.jsx)(`label`,{htmlFor:`policyDocument`,children:`Policy Document (JSON)`})}),e[36]=H):H=e[36];let U=!!y.policyDocument,W;e[37]===_?W=e[38]:(W=_(`policyDocument`),e[37]=_,e[38]=W);let G;e[39]!==U||e[40]!==W?(G=(0,p.jsx)(d,{"aria-invalid":U,className:`font-mono text-sm`,id:`policyDocument`,rows:15,...W}),e[39]=U,e[40]=W,e[41]=G):G=e[41];let K;e[42]===y.policyDocument?K=e[43]:(K=(0,p.jsx)(l,{errors:[y.policyDocument]}),e[42]=y.policyDocument,e[43]=K);let q;e[44]!==G||e[45]!==K?(q=(0,p.jsxs)(u,{children:[H,G,K]}),e[44]=G,e[45]=K,e[46]=q):q=e[46];let J;e[47]===w?J=e[48]:(J=se(w),e[47]=w,e[48]=J);let Y;e[49]===J?Y=e[50]:(Y=(0,p.jsx)(i,{commands:J}),e[49]=J,e[50]=Y);let X;e[51]===t?X=e[52]:(X=async()=>await t({to:`/iam/list-policies`}),e[51]=t,e[52]=X);let Z;e[53]!==m.isPending||e[54]!==b||e[55]!==X?(Z=(0,p.jsx)(re,{isPending:m.isPending,isSubmitting:b,onCancel:X,pendingLabel:`Creating...`,submitLabel:`Create Policy`}),e[53]=m.isPending,e[54]=b,e[55]=X,e[56]=Z):Z=e[56];let Q;e[57]!==I||e[58]!==V||e[59]!==q||e[60]!==Y||e[61]!==Z||e[62]!==A?(Q=(0,p.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:A,children:[I,V,q,Y,Z]}),e[57]=I,e[58]=V,e[59]=q,e[60]=Y,e[61]=Z,e[62]=A,e[63]=Q):Q=e[63];let $;return e[64]!==Q||e[65]!==k?($=(0,p.jsxs)(p.Fragment,{children:[D,O,k,Q]}),e[64]=Q,e[65]=k,e[66]=$):$=e[66],$}function se(e){let t=e(`policyName`),n=typeof t==`string`?t:``,r=e(`description`),i=typeof r==`string`?r:``,a=e(`policyDocument`),o=typeof a==`string`?a:``,s=[{type:`bin`,value:`AWS_PROFILE=spinifex aws iam create-policy`},{type:`flag`,value:` \\
import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./back-link.js";import{t as i}from"./cli-command-panel.js";import{o as a}from"./iam.js";import{Ir as o,Rn as s}from"./index.js";import{a as c,i as ee,n as te,t as ne}from"./zod.js";import{t as re}from"./form-actions.js";import{t as ie}from"./page-heading.js";import{a as l,r as u,t as d}from"./field.js";import{t as ae}from"./json-editor.js";import{r as oe}from"./iam2.js";var se=t(),f=e(),p=JSON.stringify({Version:`2012-10-17`,Statement:[{Effect:`Allow`,Action:`*`,Resource:`*`}]},null,2);function m(){let e=(0,se.c)(67),t=o(),m=a(),g;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(g={resolver:ne(oe),defaultValues:{policyDocument:p}},e[0]=g):g=e[0];let{handleSubmit:_,register:v,control:y,formState:ce}=ee(g),{errors:b,isSubmitting:x}=ce,S;e[1]===y?S=e[2]:(S={control:y},e[1]=y,e[2]=S);let C=c(S),w;e[3]===C?w=e[4]:(w=e=>e?C[e]:void 0,e[3]=C,e[4]=w);let T=w,E;e[5]!==m||e[6]!==t?(E=async e=>{await m.mutateAsync(e),t({to:`/iam/list-policies`})},e[5]=m,e[6]=t,e[7]=E):E=e[7];let D=E,O,k;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(O=(0,f.jsx)(r,{to:`/iam/list-policies`,children:`Back to policies`}),k=(0,f.jsx)(ie,{title:`Create Policy`}),e[8]=O,e[9]=k):(O=e[8],k=e[9]);let A;e[10]===m.error?A=e[11]:(A=m.error&&(0,f.jsx)(n,{error:m.error,msg:`Failed to create policy`}),e[10]=m.error,e[11]=A);let j;e[12]!==_||e[13]!==D?(j=_(D),e[12]=_,e[13]=D,e[14]=j):j=e[14];let M;e[15]===Symbol.for(`react.memo_cache_sentinel`)?(M=(0,f.jsx)(l,{children:(0,f.jsx)(`label`,{htmlFor:`policyName`,children:`Policy Name`})}),e[15]=M):M=e[15];let N=!!b.policyName,P;e[16]===v?P=e[17]:(P=v(`policyName`),e[16]=v,e[17]=P);let F;e[18]!==N||e[19]!==P?(F=(0,f.jsx)(s,{"aria-invalid":N,id:`policyName`,placeholder:`my-policy...`,...P}),e[18]=N,e[19]=P,e[20]=F):F=e[20];let I;e[21]===b.policyName?I=e[22]:(I=(0,f.jsx)(u,{errors:[b.policyName]}),e[21]=b.policyName,e[22]=I);let L;e[23]!==F||e[24]!==I?(L=(0,f.jsxs)(d,{children:[M,F,I]}),e[23]=F,e[24]=I,e[25]=L):L=e[25];let R;e[26]===Symbol.for(`react.memo_cache_sentinel`)?(R=(0,f.jsx)(l,{children:(0,f.jsx)(`label`,{htmlFor:`description`,children:`Description (optional)`})}),e[26]=R):R=e[26];let z;e[27]===v?z=e[28]:(z=v(`description`),e[27]=v,e[28]=z);let B;e[29]===z?B=e[30]:(B=(0,f.jsx)(s,{id:`description`,placeholder:`A brief description of this policy...`,...z}),e[29]=z,e[30]=B);let V;e[31]===b.description?V=e[32]:(V=(0,f.jsx)(u,{errors:[b.description]}),e[31]=b.description,e[32]=V);let H;e[33]!==B||e[34]!==V?(H=(0,f.jsxs)(d,{children:[R,B,V]}),e[33]=B,e[34]=V,e[35]=H):H=e[35];let U;e[36]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,f.jsx)(l,{children:(0,f.jsx)(`label`,{htmlFor:`policyDocument`,children:`Policy Document (JSON)`})}),e[36]=U):U=e[36];let W;e[37]===b.policyDocument?W=e[38]:(W=e=>{let{field:t}=e,{onBlur:n,onChange:r,ref:i,value:a}=t;return(0,f.jsx)(ae,{error:!!b.policyDocument,id:`policyDocument`,onBlur:n,onChange:r,ref:i,rows:15,value:a})},e[37]=b.policyDocument,e[38]=W);let G;e[39]!==y||e[40]!==W?(G=(0,f.jsx)(te,{control:y,name:`policyDocument`,render:W}),e[39]=y,e[40]=W,e[41]=G):G=e[41];let K;e[42]===b.policyDocument?K=e[43]:(K=(0,f.jsx)(u,{errors:[b.policyDocument]}),e[42]=b.policyDocument,e[43]=K);let q;e[44]!==G||e[45]!==K?(q=(0,f.jsxs)(d,{children:[U,G,K]}),e[44]=G,e[45]=K,e[46]=q):q=e[46];let J;e[47]===T?J=e[48]:(J=h(T),e[47]=T,e[48]=J);let Y;e[49]===J?Y=e[50]:(Y=(0,f.jsx)(i,{commands:J}),e[49]=J,e[50]=Y);let X;e[51]===t?X=e[52]:(X=async()=>await t({to:`/iam/list-policies`}),e[51]=t,e[52]=X);let Z;e[53]!==m.isPending||e[54]!==x||e[55]!==X?(Z=(0,f.jsx)(re,{isPending:m.isPending,isSubmitting:x,onCancel:X,pendingLabel:`Creating...`,submitLabel:`Create Policy`}),e[53]=m.isPending,e[54]=x,e[55]=X,e[56]=Z):Z=e[56];let Q;e[57]!==L||e[58]!==H||e[59]!==q||e[60]!==Y||e[61]!==Z||e[62]!==j?(Q=(0,f.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:j,children:[L,H,q,Y,Z]}),e[57]=L,e[58]=H,e[59]=q,e[60]=Y,e[61]=Z,e[62]=j,e[63]=Q):Q=e[63];let $;return e[64]!==Q||e[65]!==A?($=(0,f.jsxs)(f.Fragment,{children:[O,k,A,Q]}),e[64]=Q,e[65]=A,e[66]=$):$=e[66],$}function h(e){let t=e(`policyName`),n=typeof t==`string`?t:``,r=e(`description`),i=typeof r==`string`?r:``,a=e(`policyDocument`),o=typeof a==`string`?a:``,s=[{type:`bin`,value:`AWS_PROFILE=spinifex aws iam create-policy`},{type:`flag`,value:` \\
--policy-name`},{type:`value`,value:` ${n||`<PolicyName>`}`}];return i&&s.push({type:`flag`,value:` \\
--description`},{type:`value`,value:` "${i}"`}),s.push({type:`flag`,value:` \\
--policy-document`},{type:`value`,value:` '${o||`<PolicyDocument>`}'`}),[{label:`Create Policy`,parts:s}]}export{m as component};
@@ -1,4 +1,4 @@
import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./back-link.js";import{t as i}from"./cli-command-panel.js";import{s as a}from"./iam.js";import{Ir as o,Rn as s}from"./index.js";import{a as ee,i as te,t as ne}from"./zod.js";import{t as re}from"./form-actions.js";import{t as ie}from"./page-heading.js";import{a as c,r as l,t as u}from"./field.js";import{t as d}from"./textarea.js";import{i as f,t as ae}from"./iam2.js";var oe=t(),p=e();function m(){let e=(0,oe.c)(67),t=o(),m=a(),h;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(h={resolver:ne(f),defaultValues:{assumeRolePolicyDocument:ae}},e[0]=h):h=e[0];let{handleSubmit:g,register:_,control:v,formState:ce}=te(h),{errors:y,isSubmitting:b}=ce,x;e[1]===v?x=e[2]:(x={control:v},e[1]=v,e[2]=x);let S=ee(x),C;e[3]===S?C=e[4]:(C=e=>e?S[e]:void 0,e[3]=S,e[4]=C);let w=C,T;e[5]!==m||e[6]!==t?(T=async e=>{await m.mutateAsync(e),t({to:`/iam/list-roles`})},e[5]=m,e[6]=t,e[7]=T):T=e[7];let E=T,D,O;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(D=(0,p.jsx)(r,{to:`/iam/list-roles`,children:`Back to roles`}),O=(0,p.jsx)(ie,{title:`Create Role`}),e[8]=D,e[9]=O):(D=e[8],O=e[9]);let k;e[10]===m.error?k=e[11]:(k=m.error&&(0,p.jsx)(n,{error:m.error,msg:`Failed to create role`}),e[10]=m.error,e[11]=k);let A;e[12]!==g||e[13]!==E?(A=g(E),e[12]=g,e[13]=E,e[14]=A):A=e[14];let j;e[15]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,p.jsx)(c,{children:(0,p.jsx)(`label`,{htmlFor:`roleName`,children:`Role Name`})}),e[15]=j):j=e[15];let M=!!y.roleName,N;e[16]===_?N=e[17]:(N=_(`roleName`),e[16]=_,e[17]=N);let P;e[18]!==M||e[19]!==N?(P=(0,p.jsx)(s,{"aria-invalid":M,id:`roleName`,placeholder:`my-role...`,...N}),e[18]=M,e[19]=N,e[20]=P):P=e[20];let F;e[21]===y.roleName?F=e[22]:(F=(0,p.jsx)(l,{errors:[y.roleName]}),e[21]=y.roleName,e[22]=F);let I;e[23]!==P||e[24]!==F?(I=(0,p.jsxs)(u,{children:[j,P,F]}),e[23]=P,e[24]=F,e[25]=I):I=e[25];let L;e[26]===Symbol.for(`react.memo_cache_sentinel`)?(L=(0,p.jsx)(c,{children:(0,p.jsx)(`label`,{htmlFor:`description`,children:`Description (optional)`})}),e[26]=L):L=e[26];let R;e[27]===_?R=e[28]:(R=_(`description`),e[27]=_,e[28]=R);let z;e[29]===R?z=e[30]:(z=(0,p.jsx)(s,{id:`description`,placeholder:`A brief description of this role...`,...R}),e[29]=R,e[30]=z);let B;e[31]===y.description?B=e[32]:(B=(0,p.jsx)(l,{errors:[y.description]}),e[31]=y.description,e[32]=B);let V;e[33]!==z||e[34]!==B?(V=(0,p.jsxs)(u,{children:[L,z,B]}),e[33]=z,e[34]=B,e[35]=V):V=e[35];let H;e[36]===Symbol.for(`react.memo_cache_sentinel`)?(H=(0,p.jsx)(c,{children:(0,p.jsx)(`label`,{htmlFor:`assumeRolePolicyDocument`,children:`Trust Policy (JSON)`})}),e[36]=H):H=e[36];let U=!!y.assumeRolePolicyDocument,W;e[37]===_?W=e[38]:(W=_(`assumeRolePolicyDocument`),e[37]=_,e[38]=W);let G;e[39]!==U||e[40]!==W?(G=(0,p.jsx)(d,{"aria-invalid":U,className:`font-mono text-sm`,id:`assumeRolePolicyDocument`,rows:15,...W}),e[39]=U,e[40]=W,e[41]=G):G=e[41];let K;e[42]===y.assumeRolePolicyDocument?K=e[43]:(K=(0,p.jsx)(l,{errors:[y.assumeRolePolicyDocument]}),e[42]=y.assumeRolePolicyDocument,e[43]=K);let q;e[44]!==G||e[45]!==K?(q=(0,p.jsxs)(u,{children:[H,G,K]}),e[44]=G,e[45]=K,e[46]=q):q=e[46];let J;e[47]===w?J=e[48]:(J=se(w),e[47]=w,e[48]=J);let Y;e[49]===J?Y=e[50]:(Y=(0,p.jsx)(i,{commands:J}),e[49]=J,e[50]=Y);let X;e[51]===t?X=e[52]:(X=async()=>await t({to:`/iam/list-roles`}),e[51]=t,e[52]=X);let Z;e[53]!==m.isPending||e[54]!==b||e[55]!==X?(Z=(0,p.jsx)(re,{isPending:m.isPending,isSubmitting:b,onCancel:X,pendingLabel:`Creating...`,submitLabel:`Create Role`}),e[53]=m.isPending,e[54]=b,e[55]=X,e[56]=Z):Z=e[56];let Q;e[57]!==I||e[58]!==V||e[59]!==q||e[60]!==Y||e[61]!==Z||e[62]!==A?(Q=(0,p.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:A,children:[I,V,q,Y,Z]}),e[57]=I,e[58]=V,e[59]=q,e[60]=Y,e[61]=Z,e[62]=A,e[63]=Q):Q=e[63];let $;return e[64]!==Q||e[65]!==k?($=(0,p.jsxs)(p.Fragment,{children:[D,O,k,Q]}),e[64]=Q,e[65]=k,e[66]=$):$=e[66],$}function se(e){let t=e(`roleName`),n=typeof t==`string`?t:``,r=e(`description`),i=typeof r==`string`?r:``,a=e(`assumeRolePolicyDocument`),o=typeof a==`string`?a:``,s=[{type:`bin`,value:`AWS_PROFILE=spinifex aws iam create-role`},{type:`flag`,value:` \\
import{n as e,t}from"./compiler-runtime.js";import{t as n}from"./error-banner.js";import{t as r}from"./back-link.js";import{t as i}from"./cli-command-panel.js";import{s as a}from"./iam.js";import{Ir as o,Rn as s}from"./index.js";import{a as c,i as ee,n as te,t as ne}from"./zod.js";import{t as re}from"./form-actions.js";import{t as ie}from"./page-heading.js";import{a as l,r as u,t as d}from"./field.js";import{t as ae}from"./json-editor.js";import{i as oe,t as se}from"./iam2.js";var f=t(),p=e();function m(){let e=(0,f.c)(67),t=o(),m=a(),g;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(g={resolver:ne(oe),defaultValues:{assumeRolePolicyDocument:se}},e[0]=g):g=e[0];let{handleSubmit:_,register:v,control:y,formState:ce}=ee(g),{errors:b,isSubmitting:x}=ce,S;e[1]===y?S=e[2]:(S={control:y},e[1]=y,e[2]=S);let C=c(S),w;e[3]===C?w=e[4]:(w=e=>e?C[e]:void 0,e[3]=C,e[4]=w);let T=w,E;e[5]!==m||e[6]!==t?(E=async e=>{await m.mutateAsync(e),t({to:`/iam/list-roles`})},e[5]=m,e[6]=t,e[7]=E):E=e[7];let D=E,O,k;e[8]===Symbol.for(`react.memo_cache_sentinel`)?(O=(0,p.jsx)(r,{to:`/iam/list-roles`,children:`Back to roles`}),k=(0,p.jsx)(ie,{title:`Create Role`}),e[8]=O,e[9]=k):(O=e[8],k=e[9]);let A;e[10]===m.error?A=e[11]:(A=m.error&&(0,p.jsx)(n,{error:m.error,msg:`Failed to create role`}),e[10]=m.error,e[11]=A);let j;e[12]!==_||e[13]!==D?(j=_(D),e[12]=_,e[13]=D,e[14]=j):j=e[14];let M;e[15]===Symbol.for(`react.memo_cache_sentinel`)?(M=(0,p.jsx)(l,{children:(0,p.jsx)(`label`,{htmlFor:`roleName`,children:`Role Name`})}),e[15]=M):M=e[15];let N=!!b.roleName,P;e[16]===v?P=e[17]:(P=v(`roleName`),e[16]=v,e[17]=P);let F;e[18]!==N||e[19]!==P?(F=(0,p.jsx)(s,{"aria-invalid":N,id:`roleName`,placeholder:`my-role...`,...P}),e[18]=N,e[19]=P,e[20]=F):F=e[20];let I;e[21]===b.roleName?I=e[22]:(I=(0,p.jsx)(u,{errors:[b.roleName]}),e[21]=b.roleName,e[22]=I);let L;e[23]!==F||e[24]!==I?(L=(0,p.jsxs)(d,{children:[M,F,I]}),e[23]=F,e[24]=I,e[25]=L):L=e[25];let R;e[26]===Symbol.for(`react.memo_cache_sentinel`)?(R=(0,p.jsx)(l,{children:(0,p.jsx)(`label`,{htmlFor:`description`,children:`Description (optional)`})}),e[26]=R):R=e[26];let z;e[27]===v?z=e[28]:(z=v(`description`),e[27]=v,e[28]=z);let B;e[29]===z?B=e[30]:(B=(0,p.jsx)(s,{id:`description`,placeholder:`A brief description of this role...`,...z}),e[29]=z,e[30]=B);let V;e[31]===b.description?V=e[32]:(V=(0,p.jsx)(u,{errors:[b.description]}),e[31]=b.description,e[32]=V);let H;e[33]!==B||e[34]!==V?(H=(0,p.jsxs)(d,{children:[R,B,V]}),e[33]=B,e[34]=V,e[35]=H):H=e[35];let U;e[36]===Symbol.for(`react.memo_cache_sentinel`)?(U=(0,p.jsx)(l,{children:(0,p.jsx)(`label`,{htmlFor:`assumeRolePolicyDocument`,children:`Trust Policy (JSON)`})}),e[36]=U):U=e[36];let W;e[37]===b.assumeRolePolicyDocument?W=e[38]:(W=e=>{let{field:t}=e,{onBlur:n,onChange:r,ref:i,value:a}=t;return(0,p.jsx)(ae,{error:!!b.assumeRolePolicyDocument,id:`assumeRolePolicyDocument`,onBlur:n,onChange:r,ref:i,rows:15,value:a})},e[37]=b.assumeRolePolicyDocument,e[38]=W);let G;e[39]!==y||e[40]!==W?(G=(0,p.jsx)(te,{control:y,name:`assumeRolePolicyDocument`,render:W}),e[39]=y,e[40]=W,e[41]=G):G=e[41];let K;e[42]===b.assumeRolePolicyDocument?K=e[43]:(K=(0,p.jsx)(u,{errors:[b.assumeRolePolicyDocument]}),e[42]=b.assumeRolePolicyDocument,e[43]=K);let q;e[44]!==G||e[45]!==K?(q=(0,p.jsxs)(d,{children:[U,G,K]}),e[44]=G,e[45]=K,e[46]=q):q=e[46];let J;e[47]===T?J=e[48]:(J=h(T),e[47]=T,e[48]=J);let Y;e[49]===J?Y=e[50]:(Y=(0,p.jsx)(i,{commands:J}),e[49]=J,e[50]=Y);let X;e[51]===t?X=e[52]:(X=async()=>await t({to:`/iam/list-roles`}),e[51]=t,e[52]=X);let Z;e[53]!==m.isPending||e[54]!==x||e[55]!==X?(Z=(0,p.jsx)(re,{isPending:m.isPending,isSubmitting:x,onCancel:X,pendingLabel:`Creating...`,submitLabel:`Create Role`}),e[53]=m.isPending,e[54]=x,e[55]=X,e[56]=Z):Z=e[56];let Q;e[57]!==L||e[58]!==H||e[59]!==q||e[60]!==Y||e[61]!==Z||e[62]!==j?(Q=(0,p.jsxs)(`form`,{className:`max-w-4xl space-y-6`,onSubmit:j,children:[L,H,q,Y,Z]}),e[57]=L,e[58]=H,e[59]=q,e[60]=Y,e[61]=Z,e[62]=j,e[63]=Q):Q=e[63];let $;return e[64]!==Q||e[65]!==A?($=(0,p.jsxs)(p.Fragment,{children:[O,k,A,Q]}),e[64]=Q,e[65]=A,e[66]=$):$=e[66],$}function h(e){let t=e(`roleName`),n=typeof t==`string`?t:``,r=e(`description`),i=typeof r==`string`?r:``,a=e(`assumeRolePolicyDocument`),o=typeof a==`string`?a:``,s=[{type:`bin`,value:`AWS_PROFILE=spinifex aws iam create-role`},{type:`flag`,value:` \\
--role-name`},{type:`value`,value:` ${n||`<RoleName>`}`}];return i&&s.push({type:`flag`,value:` \\
--description`},{type:`value`,value:` "${i}"`}),s.push({type:`flag`,value:` \\
--assume-role-policy-document`},{type:`value`,value:` '${o||`<TrustPolicy>`}'`}),[{label:`Create Role`,parts:s}]}export{m as component};
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
import{f as e,u as t}from"./auth.js";import{t as n}from"./json.js";var r=t({userName:e().min(1,`User name is required`).max(64,`User name must be 64 characters or less`).regex(/^[\w+=,.@-]+$/,`User name contains invalid characters`),path:e().optional()}),i=t({policyName:e().min(1,`Policy name is required`).max(128,`Policy name must be 128 characters or less`).regex(/^[\w+=,.@-]+$/,`Policy name contains invalid characters`),description:e().optional(),policyDocument:n({label:`Policy document`})}),a=JSON.stringify({Version:`2012-10-17`,Statement:[{Effect:`Allow`,Principal:{Service:`ec2.amazonaws.com`},Action:`sts:AssumeRole`}]},null,2),o=t({roleName:e().min(1,`Role name is required`).max(64,`Role name must be 64 characters or less`).regex(/^[\w+=,.@-]+$/,`Role name contains invalid characters`),path:e().optional(),description:e().optional(),assumeRolePolicyDocument:n({label:`Trust policy`})}),s=t({instanceProfileName:e().min(1,`Instance profile name is required`).max(128,`Instance profile name must be 128 characters or less`).regex(/^[\w+=,.@-]+$/,`Instance profile name contains invalid characters`),path:e().optional()});export{r as a,o as i,s as n,i as r,a as t};
import{f as e,u as t}from"./auth.js";import{n}from"./json.js";var r=t({userName:e().min(1,`User name is required`).max(64,`User name must be 64 characters or less`).regex(/^[\w+=,.@-]+$/,`User name contains invalid characters`),path:e().optional()}),i=t({policyName:e().min(1,`Policy name is required`).max(128,`Policy name must be 128 characters or less`).regex(/^[\w+=,.@-]+$/,`Policy name contains invalid characters`),description:e().optional(),policyDocument:n({label:`Policy document`})}),a=JSON.stringify({Version:`2012-10-17`,Statement:[{Effect:`Allow`,Principal:{Service:`ec2.amazonaws.com`},Action:`sts:AssumeRole`}]},null,2),o=t({roleName:e().min(1,`Role name is required`).max(64,`Role name must be 64 characters or less`).regex(/^[\w+=,.@-]+$/,`Role name contains invalid characters`),path:e().optional(),description:e().optional(),assumeRolePolicyDocument:n({label:`Trust policy`})}),s=t({instanceProfileName:e().min(1,`Instance profile name is required`).max(128,`Instance profile name must be 128 characters or less`).regex(/^[\w+=,.@-]+$/,`Instance profile name contains invalid characters`),path:e().optional()});export{r as a,o as i,s as n,i as r,a as t};
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
import{n as e,t}from"./compiler-runtime.js";import{Lt as n}from"./auth.js";import{t as r}from"./textarea.js";import{t as i}from"./json.js";var a=t(),o=e();function s(e){let t=(0,a.c)(22),s,c,l,u,d,f;t[0]===e?(s=t[1],c=t[2],l=t[3],u=t[4],d=t[5],f=t[6]):({value:f,onChange:u,error:c,onBlur:l,className:s,...d}=e,t[0]=e,t[1]=s,t[2]=c,t[3]=l,t[4]=u,t[5]=d,t[6]=f);let p;t[7]!==l||t[8]!==u||t[9]!==f?(p=e=>{let t=i(f);t!==null&&t!==f&&u(t),l?.(e)},t[7]=l,t[8]=u,t[9]=f,t[10]=p):p=t[10];let m=p,h;t[11]===s?h=t[12]:(h=n(`font-mono text-sm`,s),t[11]=s,t[12]=h);let g;t[13]===u?g=t[14]:(g=e=>u(e.target.value),t[13]=u,t[14]=g);let _;return t[15]!==c||t[16]!==m||t[17]!==d||t[18]!==h||t[19]!==g||t[20]!==f?(_=(0,o.jsx)(r,{"aria-invalid":c,className:h,onBlur:m,onChange:g,value:f,...d}),t[15]=c,t[16]=m,t[17]=d,t[18]=h,t[19]=g,t[20]=f,t[21]=_):_=t[21],_}export{s as t};
+1 -1
View File
@@ -1 +1 @@
import{f as e}from"./auth.js";function t(e){try{return JSON.parse(e),!0}catch{return!1}}function n(n){let{label:r,allowEmpty:i=!1}=n;return(i?e():e().min(1,`${r} is required`)).refine(e=>i&&e.trim()===``?!0:t(e),{message:`${r} must be valid JSON`})}export{n as t};
import{f as e}from"./auth.js";function t(e){try{return JSON.parse(e),!0}catch{return!1}}function n(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return null}}function r(n){let{label:r,allowEmpty:i=!1}=n;return(i?e():e().min(1,`${r} is required`)).refine(e=>i&&e.trim()===``?!0:t(e),{message:`${r} must be valid JSON`})}export{r as n,n as t};
@@ -0,0 +1,62 @@
import { render, screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { useState } from "react"
import { describe, expect, it, vi } from "vitest"
import { JsonEditor } from "./json-editor"
function Harness({ initial }: { initial: string }) {
const [value, setValue] = useState(initial)
return (
<>
<JsonEditor aria-label="json" onChange={setValue} value={value} />
<button type="button">elsewhere</button>
</>
)
}
const blurAway = async (user: ReturnType<typeof userEvent.setup>) =>
await user.click(screen.getByRole("button", { name: "elsewhere" }))
describe("JsonEditor", () => {
it("pretty-prints valid JSON on blur", async () => {
const user = userEvent.setup()
render(<Harness initial='{"a":1}' />)
const editor = screen.getByLabelText("json")
await user.click(editor)
await blurAway(user)
expect(editor).toHaveValue('{\n "a": 1\n}')
})
it("leaves malformed JSON untouched on blur", async () => {
const user = userEvent.setup()
render(<Harness initial="{ not json" />)
const editor = screen.getByLabelText("json")
await user.click(editor)
await blurAway(user)
expect(editor).toHaveValue("{ not json")
})
it("propagates raw input through onChange", async () => {
const user = userEvent.setup()
const onChange = vi.fn()
render(<JsonEditor aria-label="json" onChange={onChange} value="" />)
await user.type(screen.getByLabelText("json"), "x")
expect(onChange).toHaveBeenCalledWith("x")
})
it("marks the field invalid when error is set", () => {
render(<JsonEditor aria-label="json" error onChange={vi.fn()} value="" />)
expect(screen.getByLabelText("json")).toHaveAttribute(
"aria-invalid",
"true",
)
})
})
@@ -0,0 +1,46 @@
import type * as React from "react"
import { Textarea } from "@/components/ui/textarea"
import { formatJson } from "@/lib/json"
import { cn } from "@/lib/utils"
interface JsonEditorProps extends Omit<
React.ComponentProps<typeof Textarea>,
"onChange" | "value"
> {
value: string
onChange: (value: string) => void
error?: boolean
}
// Controlled JSON textarea. On blur it pretty-prints valid JSON; malformed
// input is left untouched so the field's validation error stays visible. The
// plain value/onChange/error contract keeps it RHF-agnostic, so it works with
// Controller or local state and is testable in isolation.
export function JsonEditor({
value,
onChange,
error,
onBlur,
className,
...props
}: JsonEditorProps) {
const handleBlur = (event: React.FocusEvent<HTMLTextAreaElement>) => {
const formatted = formatJson(value)
if (formatted !== null && formatted !== value) {
onChange(formatted)
}
onBlur?.(event)
}
return (
<Textarea
aria-invalid={error}
className={cn("font-mono text-sm", className)}
onBlur={handleBlur}
onChange={(event) => onChange(event.target.value)}
value={value}
{...props}
/>
)
}
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest"
import { isValidJson, jsonStringSchema } from "./json"
import { formatJson, isValidJson, jsonStringSchema } from "./json"
type JsonSchema = ReturnType<typeof jsonStringSchema>
@@ -23,6 +23,17 @@ describe("isValidJson", () => {
})
})
describe("formatJson", () => {
it("pretty-prints valid JSON with a 2-space indent", () => {
expect(formatJson('{"a":1}')).toBe('{\n "a": 1\n}')
})
it("returns null for malformed JSON", () => {
expect(formatJson("{ not json")).toBeNull()
expect(formatJson("")).toBeNull()
})
})
describe("jsonStringSchema", () => {
const required = jsonStringSchema({ label: "Policy document" })
const optional = jsonStringSchema({
@@ -9,6 +9,16 @@ export function isValidJson(value: string): boolean {
}
}
// Pretty-print JSON with a 2-space indent. Returns null when the input is not
// valid JSON so callers can leave the original text untouched.
export function formatJson(value: string): string | null {
try {
return JSON.stringify(JSON.parse(value), null, 2)
} catch {
return null
}
}
// Zod field for a JSON document entered as a string. Messages derive from
// `label` so they read naturally per field. When `allowEmpty` is true an
// empty/whitespace value passes (optional field); otherwise it is required.
@@ -3,7 +3,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
import { useQuery, useSuspenseQuery } from "@tanstack/react-query"
import { ArrowUpCircle, Trash2 } from "lucide-react"
import { useEffect, useState } from "react"
import { useForm } from "react-hook-form"
import { Controller, useForm } from "react-hook-form"
import { DeleteConfirmationDialog } from "@/components/delete-confirmation-dialog"
import { DetailCard } from "@/components/detail-card"
@@ -21,7 +21,7 @@ import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Field, FieldError, FieldTitle } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { JsonEditor } from "@/components/ui/json-editor"
import { useCreateAddon, useDeleteAddon, useUpdateAddon } from "@/mutations/eks"
import {
eksAddonQueryOptions,
@@ -60,6 +60,7 @@ function AddAddonDialog({
)
const {
control,
formState: { errors },
handleSubmit,
register,
@@ -178,12 +179,21 @@ function AddAddonDialog({
<FieldTitle>
<label htmlFor="addon-config">Configuration values (JSON)</label>
</FieldTitle>
<Textarea
aria-invalid={!!errors.configurationValues}
id="addon-config"
placeholder='{"replicaCount": 2}'
rows={4}
{...register("configurationValues")}
<Controller
control={control}
name="configurationValues"
render={({ field: { onBlur, onChange, ref, value } }) => (
<JsonEditor
error={!!errors.configurationValues}
id="addon-config"
onBlur={onBlur}
onChange={onChange}
placeholder='{"replicaCount": 2}'
ref={ref}
rows={4}
value={value}
/>
)}
/>
<FieldError errors={[errors.configurationValues]} />
</Field>
@@ -313,9 +323,10 @@ function UpdateAddonDialog({
Configuration values (JSON)
</label>
</FieldTitle>
<Textarea
<JsonEditor
error={!!error}
id="update-addon-config"
onChange={(e) => setConfig(e.target.value)}
onChange={setConfig}
rows={4}
value={config}
/>
@@ -1,6 +1,6 @@
import { zodResolver } from "@hookform/resolvers/zod"
import { createFileRoute, useNavigate } from "@tanstack/react-router"
import { useForm, useWatch } from "react-hook-form"
import { Controller, useForm, useWatch } from "react-hook-form"
import { BackLink } from "@/components/back-link"
import {
@@ -12,7 +12,7 @@ import { FormActions } from "@/components/form-actions"
import { PageHeading } from "@/components/page-heading"
import { Field, FieldError, FieldTitle } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { JsonEditor } from "@/components/ui/json-editor"
import { useCreatePolicy } from "@/mutations/iam"
import { type CreatePolicyFormData, createPolicySchema } from "@/types/iam"
@@ -105,12 +105,20 @@ function CreatePolicy() {
<FieldTitle>
<label htmlFor="policyDocument">Policy Document (JSON)</label>
</FieldTitle>
<Textarea
aria-invalid={!!errors.policyDocument}
className="font-mono text-sm"
id="policyDocument"
rows={15}
{...register("policyDocument")}
<Controller
control={control}
name="policyDocument"
render={({ field: { onBlur, onChange, ref, value } }) => (
<JsonEditor
error={!!errors.policyDocument}
id="policyDocument"
onBlur={onBlur}
onChange={onChange}
ref={ref}
rows={15}
value={value}
/>
)}
/>
<FieldError errors={[errors.policyDocument]} />
</Field>
@@ -1,6 +1,6 @@
import { zodResolver } from "@hookform/resolvers/zod"
import { createFileRoute, useNavigate } from "@tanstack/react-router"
import { useForm, useWatch } from "react-hook-form"
import { Controller, useForm, useWatch } from "react-hook-form"
import { BackLink } from "@/components/back-link"
import {
@@ -12,7 +12,7 @@ import { FormActions } from "@/components/form-actions"
import { PageHeading } from "@/components/page-heading"
import { Field, FieldError, FieldTitle } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { JsonEditor } from "@/components/ui/json-editor"
import { useCreateRole } from "@/mutations/iam"
import {
type CreateRoleFormData,
@@ -93,12 +93,20 @@ function CreateRole() {
Trust Policy (JSON)
</label>
</FieldTitle>
<Textarea
aria-invalid={!!errors.assumeRolePolicyDocument}
className="font-mono text-sm"
id="assumeRolePolicyDocument"
rows={15}
{...register("assumeRolePolicyDocument")}
<Controller
control={control}
name="assumeRolePolicyDocument"
render={({ field: { onBlur, onChange, ref, value } }) => (
<JsonEditor
error={!!errors.assumeRolePolicyDocument}
id="assumeRolePolicyDocument"
onBlur={onBlur}
onChange={onChange}
ref={ref}
rows={15}
value={value}
/>
)}
/>
<FieldError errors={[errors.assumeRolePolicyDocument]} />
</Field>