orcheo-studio 0.21.3 → 0.22.2

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 (77) hide show
  1. package/.bumpversion.cfg +1 -1
  2. package/dist/assets/{arc-CI8iBP22.js → arc-De9dLqyl.js} +1 -1
  3. package/dist/assets/{architectureDiagram-VXUJARFQ-9lccnHEL.js → architectureDiagram-VXUJARFQ-hEslfmYm.js} +1 -1
  4. package/dist/assets/{blockDiagram-VD42YOAC-CJPKWPSp.js → blockDiagram-VD42YOAC-CyWSMWJa.js} +1 -1
  5. package/dist/assets/{c4Diagram-YG6GDRKO-CuW7nex5.js → c4Diagram-YG6GDRKO-CxoTVJ47.js} +1 -1
  6. package/dist/assets/channel-CzCzGn0f.js +1 -0
  7. package/dist/assets/{chunk-4BX2VUAB-BVQ3rR7o.js → chunk-4BX2VUAB-twftUw9o.js} +1 -1
  8. package/dist/assets/{chunk-55IACEB6-BTpqJ_MA.js → chunk-55IACEB6-DQZDNQUo.js} +1 -1
  9. package/dist/assets/{chunk-B4BG7PRW-Duatgdyr.js → chunk-B4BG7PRW-BYGOoNB5.js} +1 -1
  10. package/dist/assets/{chunk-DI55MBZ5-CxEvlbgl.js → chunk-DI55MBZ5-qyPhcZF3.js} +1 -1
  11. package/dist/assets/{chunk-FMBD7UC4-C07lS0oH.js → chunk-FMBD7UC4-BgUDHGEG.js} +1 -1
  12. package/dist/assets/{chunk-QN33PNHL-C09PhK0w.js → chunk-QN33PNHL-DHlMh8NC.js} +1 -1
  13. package/dist/assets/{chunk-QZHKN3VN-Dy3GgrIN.js → chunk-QZHKN3VN-Bfc8IV0O.js} +1 -1
  14. package/dist/assets/{chunk-TZMSLE5B-9HPWR_AG.js → chunk-TZMSLE5B-BqwdftTm.js} +1 -1
  15. package/dist/assets/classDiagram-2ON5EDUG-ekNI5d_4.js +1 -0
  16. package/dist/assets/classDiagram-v2-WZHVMYZB-ekNI5d_4.js +1 -0
  17. package/dist/assets/clone-DowBu1Kh.js +1 -0
  18. package/dist/assets/{cose-bilkent-S5V4N54A-B9TWyynz.js → cose-bilkent-S5V4N54A-g5agH7fS.js} +1 -1
  19. package/dist/assets/{dagre-6UL2VRFP-DFuJOuNQ.js → dagre-6UL2VRFP-ajLMaXPP.js} +1 -1
  20. package/dist/assets/{diagram-PSM6KHXK-3Jj-OXdd.js → diagram-PSM6KHXK-BndVcBhB.js} +1 -1
  21. package/dist/assets/{diagram-QEK2KX5R-DtAwZnbJ.js → diagram-QEK2KX5R-BFiIYeu7.js} +1 -1
  22. package/dist/assets/{diagram-S2PKOQOG-atZg3Snz.js → diagram-S2PKOQOG-DXy-A9pn.js} +1 -1
  23. package/dist/assets/{erDiagram-Q2GNP2WA-BoqqRkgy.js → erDiagram-Q2GNP2WA-D_Bac_58.js} +1 -1
  24. package/dist/assets/{flowDiagram-NV44I4VS-Bmg68IjU.js → flowDiagram-NV44I4VS-DhEi8qtz.js} +1 -1
  25. package/dist/assets/{ganttDiagram-JELNMOA3-53TZ4oeR.js → ganttDiagram-JELNMOA3-BtpUvOuc.js} +1 -1
  26. package/dist/assets/{gitGraphDiagram-V2S2FVAM-D1DddraJ.js → gitGraphDiagram-V2S2FVAM-MBES60cx.js} +1 -1
  27. package/dist/assets/{graph-Cb10Gpkt.js → graph-BcwvIN-m.js} +1 -1
  28. package/dist/assets/index-DLYzf0K-.js +775 -0
  29. package/dist/assets/index-DebsXgfj.css +1 -0
  30. package/dist/assets/{infoDiagram-HS3SLOUP-DxsHJs_0.js → infoDiagram-HS3SLOUP-vaIFYyC9.js} +1 -1
  31. package/dist/assets/{isUndefined-D1qU3_L5.js → isUndefined-DVO6xakl.js} +1 -1
  32. package/dist/assets/{journeyDiagram-XKPGCS4Q-CZDE4S5c.js → journeyDiagram-XKPGCS4Q-D-_q99If.js} +1 -1
  33. package/dist/assets/{kanban-definition-3W4ZIXB7-pRhzMZ6H.js → kanban-definition-3W4ZIXB7-i_AZcs_J.js} +1 -1
  34. package/dist/assets/{layout-C0AW3yEL.js → layout-CPJ9nR_z.js} +1 -1
  35. package/dist/assets/{linear-DobjN4tM.js → linear-KBGpPBiH.js} +1 -1
  36. package/dist/assets/{min-B3Oh8jmi.js → min-By5f7YdD.js} +1 -1
  37. package/dist/assets/{mindmap-definition-VGOIOE7T-g-SEeftZ.js → mindmap-definition-VGOIOE7T-DRjtRxFS.js} +1 -1
  38. package/dist/assets/{pieDiagram-ADFJNKIX-DiwhMRkx.js → pieDiagram-ADFJNKIX-BkkSglWM.js} +1 -1
  39. package/dist/assets/{quadrantDiagram-AYHSOK5B-BIZMdGTC.js → quadrantDiagram-AYHSOK5B-DY-Ng2qt.js} +1 -1
  40. package/dist/assets/{requirementDiagram-UZGBJVZJ-D5nzjn-8.js → requirementDiagram-UZGBJVZJ-B5w0etXG.js} +1 -1
  41. package/dist/assets/{sankeyDiagram-TZEHDZUN-CnCESEvf.js → sankeyDiagram-TZEHDZUN-BWntUp8b.js} +1 -1
  42. package/dist/assets/{sequenceDiagram-WL72ISMW-DKBzac_O.js → sequenceDiagram-WL72ISMW-Cl77fd7y.js} +1 -1
  43. package/dist/assets/{stateDiagram-FKZM4ZOC-CcT1-5cM.js → stateDiagram-FKZM4ZOC-B8_DoPW6.js} +1 -1
  44. package/dist/assets/stateDiagram-v2-4FDKWEC3-DHL7p-md.js +1 -0
  45. package/dist/assets/{timeline-definition-IT6M3QCI-DhFmWcr3.js → timeline-definition-IT6M3QCI-udvNLTe_.js} +1 -1
  46. package/dist/assets/{treemap-GDKQZRPO-CtMKB2mb.js → treemap-GDKQZRPO-BSvoMl74.js} +1 -1
  47. package/dist/assets/{xychartDiagram-PRI3JC2R-OhMt1B4k.js → xychartDiagram-PRI3JC2R-CTdy-FCX.js} +1 -1
  48. package/dist/index.html +2 -7
  49. package/index.html +0 -5
  50. package/package.json +1 -1
  51. package/src/App.tsx +20 -0
  52. package/src/features/chatkit/pages/public-chat.tsx +7 -3
  53. package/src/features/workflow/data/templates/candidate-badges.ts +5 -0
  54. package/src/features/workflow/data/workflow-types.ts +1 -0
  55. package/src/features/workflow/lib/workflow-storage-api.ts +35 -4
  56. package/src/features/workflow/lib/workflow-storage-helpers.ts +10 -3
  57. package/src/features/workflow/lib/workflow-storage.ts +2 -1
  58. package/src/features/workflow/lib/workflow-storage.types.ts +9 -0
  59. package/src/features/workflow/pages/workflow-gallery/create-team-dialog.tsx +137 -0
  60. package/src/features/workflow/pages/workflow-gallery/onboard-team-dialog.tsx +57 -0
  61. package/src/features/workflow/pages/workflow-gallery/team-section.tsx +58 -0
  62. package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery-actions.ts +124 -21
  63. package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery-state.ts +22 -2
  64. package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery.ts +2 -0
  65. package/src/features/workflow/pages/workflow-gallery/workflow-card.tsx +26 -5
  66. package/src/features/workflow/pages/workflow-gallery/workflow-gallery-tabs.tsx +91 -16
  67. package/src/features/workflow/pages/workflow-gallery.tsx +45 -1
  68. package/src/lib/api.ts +13 -1
  69. package/src/lib/workspace-routing.ts +15 -0
  70. package/src/main.tsx +7 -0
  71. package/dist/assets/channel-C-DaAO-p.js +0 -1
  72. package/dist/assets/classDiagram-2ON5EDUG-B2LNkTFb.js +0 -1
  73. package/dist/assets/classDiagram-v2-WZHVMYZB-B2LNkTFb.js +0 -1
  74. package/dist/assets/clone-DcGVOYZT.js +0 -1
  75. package/dist/assets/index-B0_h2wCA.css +0 -1
  76. package/dist/assets/index-DmIcay1T.js +0 -775
  77. package/dist/assets/stateDiagram-v2-4FDKWEC3-Cle1H2JX.js +0 -1
@@ -1,4 +1,4 @@
1
- import{s as gi,g as xi,q as Xt,p as di,a as fi,b as pi,_ as a,l as Nt,H as mi,e as yi,y as bi,F as St,i as Ai,D as Yt,E as wi,K as Ci,aI as Si,R as Wt}from"./index-DmIcay1T.js";import{i as _i}from"./init-Gi6I4Gst.js";import{o as ki}from"./ordinal-Cboi1Yqb.js";import{l as zt}from"./linear-DobjN4tM.js";import"./defaultLocale-C4B-KCzX.js";function Ri(e,t,i){e=+e,t=+t,i=(n=arguments.length)<2?(t=e,e=0,1):n<3?1:+i;for(var s=-1,n=Math.max(0,Math.ceil((t-e)/i))|0,o=new Array(n);++s<n;)o[s]=e+s*i;return o}function yt(){var e=ki().unknown(void 0),t=e.domain,i=e.range,s=0,n=1,o,g,m=!1,p=0,k=0,v=.5;delete e.unknown;function C(){var b=t().length,E=n<s,D=E?n:s,P=E?s:n;o=(P-D)/Math.max(1,b-p+k*2),m&&(o=Math.floor(o)),D+=(P-D-o*(b-p))*v,g=o*(1-p),m&&(D=Math.round(D),g=Math.round(g));var I=Ri(b).map(function(y){return D+o*y});return i(E?I.reverse():I)}return e.domain=function(b){return arguments.length?(t(b),C()):t()},e.range=function(b){return arguments.length?([s,n]=b,s=+s,n=+n,C()):[s,n]},e.rangeRound=function(b){return[s,n]=b,s=+s,n=+n,m=!0,C()},e.bandwidth=function(){return g},e.step=function(){return o},e.round=function(b){return arguments.length?(m=!!b,C()):m},e.padding=function(b){return arguments.length?(p=Math.min(1,k=+b),C()):p},e.paddingInner=function(b){return arguments.length?(p=Math.min(1,b),C()):p},e.paddingOuter=function(b){return arguments.length?(k=+b,C()):k},e.align=function(b){return arguments.length?(v=Math.max(0,Math.min(1,b)),C()):v},e.copy=function(){return yt(t(),[s,n]).round(m).paddingInner(p).paddingOuter(k).align(v)},_i.apply(C(),arguments)}var bt=(function(){var e=a(function(F,h,u,x){for(u=u||{},x=F.length;x--;u[F[x]]=h);return u},"o"),t=[1,10,12,14,16,18,19,21,23],i=[2,6],s=[1,3],n=[1,5],o=[1,6],g=[1,7],m=[1,5,10,12,14,16,18,19,21,23,34,35,36],p=[1,25],k=[1,26],v=[1,28],C=[1,29],b=[1,30],E=[1,31],D=[1,32],P=[1,33],I=[1,34],y=[1,35],_=[1,36],c=[1,37],W=[1,43],z=[1,42],U=[1,47],X=[1,50],l=[1,10,12,14,16,18,19,21,23,34,35,36],L=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36],S=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36,41,42,43,44,45,46,47,48,49,50],R=[1,64],$={trace:a(function(){},"trace"),yy:{},symbols_:{error:2,start:3,eol:4,XYCHART:5,chartConfig:6,document:7,CHART_ORIENTATION:8,statement:9,title:10,text:11,X_AXIS:12,parseXAxis:13,Y_AXIS:14,parseYAxis:15,LINE:16,plotData:17,BAR:18,acc_title:19,acc_title_value:20,acc_descr:21,acc_descr_value:22,acc_descr_multiline_value:23,SQUARE_BRACES_START:24,commaSeparatedNumbers:25,SQUARE_BRACES_END:26,NUMBER_WITH_DECIMAL:27,COMMA:28,xAxisData:29,bandData:30,ARROW_DELIMITER:31,commaSeparatedTexts:32,yAxisData:33,NEWLINE:34,SEMI:35,EOF:36,alphaNum:37,STR:38,MD_STR:39,alphaNumToken:40,AMP:41,NUM:42,ALPHA:43,PLUS:44,EQUALS:45,MULT:46,DOT:47,BRKT:48,MINUS:49,UNDERSCORE:50,$accept:0,$end:1},terminals_:{2:"error",5:"XYCHART",8:"CHART_ORIENTATION",10:"title",12:"X_AXIS",14:"Y_AXIS",16:"LINE",18:"BAR",19:"acc_title",20:"acc_title_value",21:"acc_descr",22:"acc_descr_value",23:"acc_descr_multiline_value",24:"SQUARE_BRACES_START",26:"SQUARE_BRACES_END",27:"NUMBER_WITH_DECIMAL",28:"COMMA",31:"ARROW_DELIMITER",34:"NEWLINE",35:"SEMI",36:"EOF",38:"STR",39:"MD_STR",41:"AMP",42:"NUM",43:"ALPHA",44:"PLUS",45:"EQUALS",46:"MULT",47:"DOT",48:"BRKT",49:"MINUS",50:"UNDERSCORE"},productions_:[0,[3,2],[3,3],[3,2],[3,1],[6,1],[7,0],[7,2],[9,2],[9,2],[9,2],[9,2],[9,2],[9,3],[9,2],[9,3],[9,2],[9,2],[9,1],[17,3],[25,3],[25,1],[13,1],[13,2],[13,1],[29,1],[29,3],[30,3],[32,3],[32,1],[15,1],[15,2],[15,1],[33,3],[4,1],[4,1],[4,1],[11,1],[11,1],[11,1],[37,1],[37,2],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1]],performAction:a(function(h,u,x,d,w,r,at){var f=r.length-1;switch(w){case 5:d.setOrientation(r[f]);break;case 9:d.setDiagramTitle(r[f].text.trim());break;case 12:d.setLineData({text:"",type:"text"},r[f]);break;case 13:d.setLineData(r[f-1],r[f]);break;case 14:d.setBarData({text:"",type:"text"},r[f]);break;case 15:d.setBarData(r[f-1],r[f]);break;case 16:this.$=r[f].trim(),d.setAccTitle(this.$);break;case 17:case 18:this.$=r[f].trim(),d.setAccDescription(this.$);break;case 19:this.$=r[f-1];break;case 20:this.$=[Number(r[f-2]),...r[f]];break;case 21:this.$=[Number(r[f])];break;case 22:d.setXAxisTitle(r[f]);break;case 23:d.setXAxisTitle(r[f-1]);break;case 24:d.setXAxisTitle({type:"text",text:""});break;case 25:d.setXAxisBand(r[f]);break;case 26:d.setXAxisRangeData(Number(r[f-2]),Number(r[f]));break;case 27:this.$=r[f-1];break;case 28:this.$=[r[f-2],...r[f]];break;case 29:this.$=[r[f]];break;case 30:d.setYAxisTitle(r[f]);break;case 31:d.setYAxisTitle(r[f-1]);break;case 32:d.setYAxisTitle({type:"text",text:""});break;case 33:d.setYAxisRangeData(Number(r[f-2]),Number(r[f]));break;case 37:this.$={text:r[f],type:"text"};break;case 38:this.$={text:r[f],type:"text"};break;case 39:this.$={text:r[f],type:"markdown"};break;case 40:this.$=r[f];break;case 41:this.$=r[f-1]+""+r[f];break}},"anonymous"),table:[e(t,i,{3:1,4:2,7:4,5:s,34:n,35:o,36:g}),{1:[3]},e(t,i,{4:2,7:4,3:8,5:s,34:n,35:o,36:g}),e(t,i,{4:2,7:4,6:9,3:10,5:s,8:[1,11],34:n,35:o,36:g}),{1:[2,4],9:12,10:[1,13],12:[1,14],14:[1,15],16:[1,16],18:[1,17],19:[1,18],21:[1,19],23:[1,20]},e(m,[2,34]),e(m,[2,35]),e(m,[2,36]),{1:[2,1]},e(t,i,{4:2,7:4,3:21,5:s,34:n,35:o,36:g}),{1:[2,3]},e(m,[2,5]),e(t,[2,7],{4:22,34:n,35:o,36:g}),{11:23,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:39,13:38,24:W,27:z,29:40,30:41,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:45,15:44,27:U,33:46,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:49,17:48,24:X,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:52,17:51,24:X,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{20:[1,53]},{22:[1,54]},e(l,[2,18]),{1:[2,2]},e(l,[2,8]),e(l,[2,9]),e(L,[2,37],{40:55,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c}),e(L,[2,38]),e(L,[2,39]),e(S,[2,40]),e(S,[2,42]),e(S,[2,43]),e(S,[2,44]),e(S,[2,45]),e(S,[2,46]),e(S,[2,47]),e(S,[2,48]),e(S,[2,49]),e(S,[2,50]),e(S,[2,51]),e(l,[2,10]),e(l,[2,22],{30:41,29:56,24:W,27:z}),e(l,[2,24]),e(l,[2,25]),{31:[1,57]},{11:59,32:58,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},e(l,[2,11]),e(l,[2,30],{33:60,27:U}),e(l,[2,32]),{31:[1,61]},e(l,[2,12]),{17:62,24:X},{25:63,27:R},e(l,[2,14]),{17:65,24:X},e(l,[2,16]),e(l,[2,17]),e(S,[2,41]),e(l,[2,23]),{27:[1,66]},{26:[1,67]},{26:[2,29],28:[1,68]},e(l,[2,31]),{27:[1,69]},e(l,[2,13]),{26:[1,70]},{26:[2,21],28:[1,71]},e(l,[2,15]),e(l,[2,26]),e(l,[2,27]),{11:59,32:72,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},e(l,[2,33]),e(l,[2,19]),{25:73,27:R},{26:[2,28]},{26:[2,20]}],defaultActions:{8:[2,1],10:[2,3],21:[2,2],72:[2,28],73:[2,20]},parseError:a(function(h,u){if(u.recoverable)this.trace(h);else{var x=new Error(h);throw x.hash=u,x}},"parseError"),parse:a(function(h){var u=this,x=[0],d=[],w=[null],r=[],at=this.table,f="",lt=0,It=0,hi=2,Mt=1,li=r.slice.call(arguments,1),T=Object.create(this.lexer),Y={yy:{}};for(var dt in this.yy)Object.prototype.hasOwnProperty.call(this.yy,dt)&&(Y.yy[dt]=this.yy[dt]);T.setInput(h,Y.yy),Y.yy.lexer=T,Y.yy.parser=this,typeof T.yylloc>"u"&&(T.yylloc={});var ft=T.yylloc;r.push(ft);var ci=T.options&&T.options.ranges;typeof Y.yy.parseError=="function"?this.parseError=Y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function ui(V){x.length=x.length-2*V,w.length=w.length-V,r.length=r.length-V}a(ui,"popStack");function Vt(){var V;return V=d.pop()||T.lex()||Mt,typeof V!="number"&&(V instanceof Array&&(d=V,V=d.pop()),V=u.symbols_[V]||V),V}a(Vt,"lex");for(var M,H,B,pt,q={},ct,O,Bt,ut;;){if(H=x[x.length-1],this.defaultActions[H]?B=this.defaultActions[H]:((M===null||typeof M>"u")&&(M=Vt()),B=at[H]&&at[H][M]),typeof B>"u"||!B.length||!B[0]){var mt="";ut=[];for(ct in at[H])this.terminals_[ct]&&ct>hi&&ut.push("'"+this.terminals_[ct]+"'");T.showPosition?mt="Parse error on line "+(lt+1)+`:
1
+ import{s as gi,g as xi,q as Xt,p as di,a as fi,b as pi,_ as a,l as Nt,H as mi,e as yi,y as bi,F as St,i as Ai,D as Yt,E as wi,K as Ci,aI as Si,R as Wt}from"./index-DLYzf0K-.js";import{i as _i}from"./init-Gi6I4Gst.js";import{o as ki}from"./ordinal-Cboi1Yqb.js";import{l as zt}from"./linear-KBGpPBiH.js";import"./defaultLocale-C4B-KCzX.js";function Ri(e,t,i){e=+e,t=+t,i=(n=arguments.length)<2?(t=e,e=0,1):n<3?1:+i;for(var s=-1,n=Math.max(0,Math.ceil((t-e)/i))|0,o=new Array(n);++s<n;)o[s]=e+s*i;return o}function yt(){var e=ki().unknown(void 0),t=e.domain,i=e.range,s=0,n=1,o,g,m=!1,p=0,k=0,v=.5;delete e.unknown;function C(){var b=t().length,E=n<s,D=E?n:s,P=E?s:n;o=(P-D)/Math.max(1,b-p+k*2),m&&(o=Math.floor(o)),D+=(P-D-o*(b-p))*v,g=o*(1-p),m&&(D=Math.round(D),g=Math.round(g));var I=Ri(b).map(function(y){return D+o*y});return i(E?I.reverse():I)}return e.domain=function(b){return arguments.length?(t(b),C()):t()},e.range=function(b){return arguments.length?([s,n]=b,s=+s,n=+n,C()):[s,n]},e.rangeRound=function(b){return[s,n]=b,s=+s,n=+n,m=!0,C()},e.bandwidth=function(){return g},e.step=function(){return o},e.round=function(b){return arguments.length?(m=!!b,C()):m},e.padding=function(b){return arguments.length?(p=Math.min(1,k=+b),C()):p},e.paddingInner=function(b){return arguments.length?(p=Math.min(1,b),C()):p},e.paddingOuter=function(b){return arguments.length?(k=+b,C()):k},e.align=function(b){return arguments.length?(v=Math.max(0,Math.min(1,b)),C()):v},e.copy=function(){return yt(t(),[s,n]).round(m).paddingInner(p).paddingOuter(k).align(v)},_i.apply(C(),arguments)}var bt=(function(){var e=a(function(F,h,u,x){for(u=u||{},x=F.length;x--;u[F[x]]=h);return u},"o"),t=[1,10,12,14,16,18,19,21,23],i=[2,6],s=[1,3],n=[1,5],o=[1,6],g=[1,7],m=[1,5,10,12,14,16,18,19,21,23,34,35,36],p=[1,25],k=[1,26],v=[1,28],C=[1,29],b=[1,30],E=[1,31],D=[1,32],P=[1,33],I=[1,34],y=[1,35],_=[1,36],c=[1,37],W=[1,43],z=[1,42],U=[1,47],X=[1,50],l=[1,10,12,14,16,18,19,21,23,34,35,36],L=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36],S=[1,10,12,14,16,18,19,21,23,24,26,27,28,34,35,36,41,42,43,44,45,46,47,48,49,50],R=[1,64],$={trace:a(function(){},"trace"),yy:{},symbols_:{error:2,start:3,eol:4,XYCHART:5,chartConfig:6,document:7,CHART_ORIENTATION:8,statement:9,title:10,text:11,X_AXIS:12,parseXAxis:13,Y_AXIS:14,parseYAxis:15,LINE:16,plotData:17,BAR:18,acc_title:19,acc_title_value:20,acc_descr:21,acc_descr_value:22,acc_descr_multiline_value:23,SQUARE_BRACES_START:24,commaSeparatedNumbers:25,SQUARE_BRACES_END:26,NUMBER_WITH_DECIMAL:27,COMMA:28,xAxisData:29,bandData:30,ARROW_DELIMITER:31,commaSeparatedTexts:32,yAxisData:33,NEWLINE:34,SEMI:35,EOF:36,alphaNum:37,STR:38,MD_STR:39,alphaNumToken:40,AMP:41,NUM:42,ALPHA:43,PLUS:44,EQUALS:45,MULT:46,DOT:47,BRKT:48,MINUS:49,UNDERSCORE:50,$accept:0,$end:1},terminals_:{2:"error",5:"XYCHART",8:"CHART_ORIENTATION",10:"title",12:"X_AXIS",14:"Y_AXIS",16:"LINE",18:"BAR",19:"acc_title",20:"acc_title_value",21:"acc_descr",22:"acc_descr_value",23:"acc_descr_multiline_value",24:"SQUARE_BRACES_START",26:"SQUARE_BRACES_END",27:"NUMBER_WITH_DECIMAL",28:"COMMA",31:"ARROW_DELIMITER",34:"NEWLINE",35:"SEMI",36:"EOF",38:"STR",39:"MD_STR",41:"AMP",42:"NUM",43:"ALPHA",44:"PLUS",45:"EQUALS",46:"MULT",47:"DOT",48:"BRKT",49:"MINUS",50:"UNDERSCORE"},productions_:[0,[3,2],[3,3],[3,2],[3,1],[6,1],[7,0],[7,2],[9,2],[9,2],[9,2],[9,2],[9,2],[9,3],[9,2],[9,3],[9,2],[9,2],[9,1],[17,3],[25,3],[25,1],[13,1],[13,2],[13,1],[29,1],[29,3],[30,3],[32,3],[32,1],[15,1],[15,2],[15,1],[33,3],[4,1],[4,1],[4,1],[11,1],[11,1],[11,1],[37,1],[37,2],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1],[40,1]],performAction:a(function(h,u,x,d,w,r,at){var f=r.length-1;switch(w){case 5:d.setOrientation(r[f]);break;case 9:d.setDiagramTitle(r[f].text.trim());break;case 12:d.setLineData({text:"",type:"text"},r[f]);break;case 13:d.setLineData(r[f-1],r[f]);break;case 14:d.setBarData({text:"",type:"text"},r[f]);break;case 15:d.setBarData(r[f-1],r[f]);break;case 16:this.$=r[f].trim(),d.setAccTitle(this.$);break;case 17:case 18:this.$=r[f].trim(),d.setAccDescription(this.$);break;case 19:this.$=r[f-1];break;case 20:this.$=[Number(r[f-2]),...r[f]];break;case 21:this.$=[Number(r[f])];break;case 22:d.setXAxisTitle(r[f]);break;case 23:d.setXAxisTitle(r[f-1]);break;case 24:d.setXAxisTitle({type:"text",text:""});break;case 25:d.setXAxisBand(r[f]);break;case 26:d.setXAxisRangeData(Number(r[f-2]),Number(r[f]));break;case 27:this.$=r[f-1];break;case 28:this.$=[r[f-2],...r[f]];break;case 29:this.$=[r[f]];break;case 30:d.setYAxisTitle(r[f]);break;case 31:d.setYAxisTitle(r[f-1]);break;case 32:d.setYAxisTitle({type:"text",text:""});break;case 33:d.setYAxisRangeData(Number(r[f-2]),Number(r[f]));break;case 37:this.$={text:r[f],type:"text"};break;case 38:this.$={text:r[f],type:"text"};break;case 39:this.$={text:r[f],type:"markdown"};break;case 40:this.$=r[f];break;case 41:this.$=r[f-1]+""+r[f];break}},"anonymous"),table:[e(t,i,{3:1,4:2,7:4,5:s,34:n,35:o,36:g}),{1:[3]},e(t,i,{4:2,7:4,3:8,5:s,34:n,35:o,36:g}),e(t,i,{4:2,7:4,6:9,3:10,5:s,8:[1,11],34:n,35:o,36:g}),{1:[2,4],9:12,10:[1,13],12:[1,14],14:[1,15],16:[1,16],18:[1,17],19:[1,18],21:[1,19],23:[1,20]},e(m,[2,34]),e(m,[2,35]),e(m,[2,36]),{1:[2,1]},e(t,i,{4:2,7:4,3:21,5:s,34:n,35:o,36:g}),{1:[2,3]},e(m,[2,5]),e(t,[2,7],{4:22,34:n,35:o,36:g}),{11:23,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:39,13:38,24:W,27:z,29:40,30:41,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:45,15:44,27:U,33:46,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:49,17:48,24:X,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{11:52,17:51,24:X,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},{20:[1,53]},{22:[1,54]},e(l,[2,18]),{1:[2,2]},e(l,[2,8]),e(l,[2,9]),e(L,[2,37],{40:55,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c}),e(L,[2,38]),e(L,[2,39]),e(S,[2,40]),e(S,[2,42]),e(S,[2,43]),e(S,[2,44]),e(S,[2,45]),e(S,[2,46]),e(S,[2,47]),e(S,[2,48]),e(S,[2,49]),e(S,[2,50]),e(S,[2,51]),e(l,[2,10]),e(l,[2,22],{30:41,29:56,24:W,27:z}),e(l,[2,24]),e(l,[2,25]),{31:[1,57]},{11:59,32:58,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},e(l,[2,11]),e(l,[2,30],{33:60,27:U}),e(l,[2,32]),{31:[1,61]},e(l,[2,12]),{17:62,24:X},{25:63,27:R},e(l,[2,14]),{17:65,24:X},e(l,[2,16]),e(l,[2,17]),e(S,[2,41]),e(l,[2,23]),{27:[1,66]},{26:[1,67]},{26:[2,29],28:[1,68]},e(l,[2,31]),{27:[1,69]},e(l,[2,13]),{26:[1,70]},{26:[2,21],28:[1,71]},e(l,[2,15]),e(l,[2,26]),e(l,[2,27]),{11:59,32:72,37:24,38:p,39:k,40:27,41:v,42:C,43:b,44:E,45:D,46:P,47:I,48:y,49:_,50:c},e(l,[2,33]),e(l,[2,19]),{25:73,27:R},{26:[2,28]},{26:[2,20]}],defaultActions:{8:[2,1],10:[2,3],21:[2,2],72:[2,28],73:[2,20]},parseError:a(function(h,u){if(u.recoverable)this.trace(h);else{var x=new Error(h);throw x.hash=u,x}},"parseError"),parse:a(function(h){var u=this,x=[0],d=[],w=[null],r=[],at=this.table,f="",lt=0,It=0,hi=2,Mt=1,li=r.slice.call(arguments,1),T=Object.create(this.lexer),Y={yy:{}};for(var dt in this.yy)Object.prototype.hasOwnProperty.call(this.yy,dt)&&(Y.yy[dt]=this.yy[dt]);T.setInput(h,Y.yy),Y.yy.lexer=T,Y.yy.parser=this,typeof T.yylloc>"u"&&(T.yylloc={});var ft=T.yylloc;r.push(ft);var ci=T.options&&T.options.ranges;typeof Y.yy.parseError=="function"?this.parseError=Y.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;function ui(V){x.length=x.length-2*V,w.length=w.length-V,r.length=r.length-V}a(ui,"popStack");function Vt(){var V;return V=d.pop()||T.lex()||Mt,typeof V!="number"&&(V instanceof Array&&(d=V,V=d.pop()),V=u.symbols_[V]||V),V}a(Vt,"lex");for(var M,H,B,pt,q={},ct,O,Bt,ut;;){if(H=x[x.length-1],this.defaultActions[H]?B=this.defaultActions[H]:((M===null||typeof M>"u")&&(M=Vt()),B=at[H]&&at[H][M]),typeof B>"u"||!B.length||!B[0]){var mt="";ut=[];for(ct in at[H])this.terminals_[ct]&&ct>hi&&ut.push("'"+this.terminals_[ct]+"'");T.showPosition?mt="Parse error on line "+(lt+1)+`:
2
2
  `+T.showPosition()+`
3
3
  Expecting `+ut.join(", ")+", got '"+(this.terminals_[M]||M)+"'":mt="Parse error on line "+(lt+1)+": Unexpected "+(M==Mt?"end of input":"'"+(this.terminals_[M]||M)+"'"),this.parseError(mt,{text:T.match,token:this.terminals_[M]||M,line:T.yylineno,loc:ft,expected:ut})}if(B[0]instanceof Array&&B.length>1)throw new Error("Parse Error: multiple actions possible at state: "+H+", token: "+M);switch(B[0]){case 1:x.push(M),w.push(T.yytext),r.push(T.yylloc),x.push(B[1]),M=null,It=T.yyleng,f=T.yytext,lt=T.yylineno,ft=T.yylloc;break;case 2:if(O=this.productions_[B[1]][1],q.$=w[w.length-O],q._$={first_line:r[r.length-(O||1)].first_line,last_line:r[r.length-1].last_line,first_column:r[r.length-(O||1)].first_column,last_column:r[r.length-1].last_column},ci&&(q._$.range=[r[r.length-(O||1)].range[0],r[r.length-1].range[1]]),pt=this.performAction.apply(q,[f,It,lt,Y.yy,B[1],w,r].concat(li)),typeof pt<"u")return pt;O&&(x=x.slice(0,-1*O*2),w=w.slice(0,-1*O),r=r.slice(0,-1*O)),x.push(this.productions_[B[1]][0]),w.push(q.$),r.push(q._$),Bt=at[x[x.length-2]][x[x.length-1]],x.push(Bt);break;case 3:return!0}}return!0},"parse")},Et=(function(){var F={EOF:1,parseError:a(function(u,x){if(this.yy.parser)this.yy.parser.parseError(u,x);else throw new Error(u)},"parseError"),setInput:a(function(h,u){return this.yy=u||this.yy||{},this._input=h,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},"setInput"),input:a(function(){var h=this._input[0];this.yytext+=h,this.yyleng++,this.offset++,this.match+=h,this.matched+=h;var u=h.match(/(?:\r\n?|\n).*/g);return u?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),h},"input"),unput:a(function(h){var u=h.length,x=h.split(/(?:\r\n?|\n)/g);this._input=h+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-u),this.offset-=u;var d=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),x.length-1&&(this.yylineno-=x.length-1);var w=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:x?(x.length===d.length?this.yylloc.first_column:0)+d[d.length-x.length].length-x[0].length:this.yylloc.first_column-u},this.options.ranges&&(this.yylloc.range=[w[0],w[0]+this.yyleng-u]),this.yyleng=this.yytext.length,this},"unput"),more:a(function(){return this._more=!0,this},"more"),reject:a(function(){if(this.options.backtrack_lexer)this._backtrack=!0;else return this.parseError("Lexical error on line "+(this.yylineno+1)+`. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).
4
4
  `+this.showPosition(),{text:"",token:null,line:this.yylineno});return this},"reject"),less:a(function(h){this.unput(this.match.slice(h))},"less"),pastInput:a(function(){var h=this.matched.substr(0,this.matched.length-this.match.length);return(h.length>20?"...":"")+h.substr(-20).replace(/\n/g,"")},"pastInput"),upcomingInput:a(function(){var h=this.match;return h.length<20&&(h+=this._input.substr(0,20-h.length)),(h.substr(0,20)+(h.length>20?"...":"")).replace(/\n/g,"")},"upcomingInput"),showPosition:a(function(){var h=this.pastInput(),u=new Array(h.length+1).join("-");return h+this.upcomingInput()+`
package/dist/index.html CHANGED
@@ -5,15 +5,10 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.ico" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>Orcheo</title>
8
- <script type="module" crossorigin src="/assets/index-DmIcay1T.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-B0_h2wCA.css">
8
+ <script type="module" crossorigin src="/assets/index-DLYzf0K-.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-DebsXgfj.css">
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
13
- <script
14
- async
15
- src="%VITE_ORCHEO_BACKEND_URL%/api/chatkit/assets/chatkit.js"
16
- crossorigin="anonymous"
17
- ></script>
18
13
  </body>
19
14
  </html>
package/index.html CHANGED
@@ -8,11 +8,6 @@
8
8
  </head>
9
9
  <body>
10
10
  <div id="root"></div>
11
- <script
12
- async
13
- src="%VITE_ORCHEO_BACKEND_URL%/api/chatkit/assets/chatkit.js"
14
- crossorigin="anonymous"
15
- ></script>
16
11
  <script type="module" src="/src/main.tsx"></script>
17
12
  </body>
18
13
  </html>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "orcheo-studio",
3
3
  "private": false,
4
- "version": "0.21.3",
4
+ "version": "0.22.2",
5
5
  "type": "module",
6
6
  "description": "Orcheo UI for Orcheo workflow orchestration platform",
7
7
  "keywords": [
package/src/App.tsx CHANGED
@@ -2,6 +2,7 @@ import { useLayoutEffect } from "react";
2
2
  import {
3
3
  BrowserRouter as Router,
4
4
  Navigate,
5
+ Outlet,
5
6
  Route,
6
7
  Routes,
7
8
  useParams,
@@ -22,6 +23,7 @@ import {
22
23
  setSelectedWorkspaceSlug,
23
24
  } from "@/lib/workspace-session";
24
25
  import { getWorkspaceGalleryPath } from "@/lib/workspace-routing";
26
+ import { WorkspaceBootstrapGate } from "@features/shared/components/workspace-bootstrap-gate";
25
27
 
26
28
  const syncWorkspaceSlug = (workspaceSlug?: string) => {
27
29
  if (!workspaceSlug) {
@@ -56,9 +58,18 @@ function WorkspaceManagementRoute() {
56
58
  return <WorkspaceManagement />;
57
59
  }
58
60
 
61
+ function RequireWorkspace() {
62
+ return (
63
+ <WorkspaceBootstrapGate>
64
+ <Outlet />
65
+ </WorkspaceBootstrapGate>
66
+ );
67
+ }
68
+
59
69
  function WorkspaceWorkflowRoute() {
60
70
  const { workspaceSlug, workflowId } = useParams<{
61
71
  workspaceSlug?: string;
72
+ teamSlug?: string;
62
73
  workflowId?: string;
63
74
  }>();
64
75
  useLayoutEffect(() => {
@@ -81,8 +92,12 @@ export default function OrcheoStudioApp() {
81
92
 
82
93
  <Route path="/auth/callback" element={<OAuthCallback />} />
83
94
  <Route path="/chat/:workflowId" element={<PublicChatPage />} />
95
+ <Route path="/chat/team/:teamSlug/:workflowId" element={<PublicChatPage />} />
96
+ <Route path="/chat/:workspaceSlug/:workflowId" element={<PublicChatPage />} />
97
+ <Route path="/chat/:workspaceSlug/team/:teamSlug/:workflowId" element={<PublicChatPage />} />
84
98
 
85
99
  <Route element={<RequireAuth />}>
100
+ <Route element={<RequireWorkspace />}>
86
101
  <Route path="/" element={<WorkspaceHomeRedirect />} />
87
102
  <Route
88
103
  path="/:workspaceSlug"
@@ -98,6 +113,10 @@ export default function OrcheoStudioApp() {
98
113
  path="/:workspaceSlug/new"
99
114
  element={<WorkspaceWorkflowRoute />}
100
115
  />
116
+ <Route
117
+ path="/:workspaceSlug/team/:teamSlug/:workflowId"
118
+ element={<WorkspaceWorkflowRoute />}
119
+ />
101
120
  <Route
102
121
  path="/:workspaceSlug/:workflowId"
103
122
  element={<WorkspaceWorkflowRoute />}
@@ -106,6 +125,7 @@ export default function OrcheoStudioApp() {
106
125
  <Route path="/profile" element={<Profile />} />
107
126
 
108
127
  <Route path="/settings" element={<Settings />} />
128
+ </Route>
109
129
  </Route>
110
130
  </Routes>
111
131
  <Toaster />
@@ -34,7 +34,11 @@ const sanitizeWorkflowNameForEmail = (value: string): string =>
34
34
  .trim();
35
35
 
36
36
  export default function PublicChatPage() {
37
- const { workflowId } = useParams<{ workflowId: string }>();
37
+ const { workflowId, workspaceSlug, teamSlug } = useParams<{
38
+ workflowId: string;
39
+ workspaceSlug?: string;
40
+ teamSlug?: string;
41
+ }>();
38
42
  const location = useLocation();
39
43
  const [workflowState, setWorkflowState] = useState<WorkflowState>({
40
44
  status: workflowId ? "loading" : "error",
@@ -67,7 +71,7 @@ export default function PublicChatPage() {
67
71
  let cancelled = false;
68
72
  setWorkflowState({ status: "loading" });
69
73
 
70
- fetchPublicWorkflow(workflowId)
74
+ fetchPublicWorkflow(workflowId, { workspaceSlug, teamSlug })
71
75
  .then((workflow) => {
72
76
  if (cancelled) {
73
77
  return;
@@ -118,7 +122,7 @@ export default function PublicChatPage() {
118
122
  return () => {
119
123
  cancelled = true;
120
124
  };
121
- }, [workflowId]);
125
+ }, [workflowId, workspaceSlug, teamSlug]);
122
126
 
123
127
  const contactHref = useMemo(() => {
124
128
  if (workflowState.status !== "ready") {
@@ -132,6 +132,11 @@ export const getCandidateBadgeDefinition = (
132
132
  ): CandidateBadgeDefinition | undefined =>
133
133
  candidateBadges.find((badge) => badge.workflow.id === workflowId);
134
134
 
135
+ export const getCandidateBadgeByHandle = (
136
+ handle: string,
137
+ ): CandidateBadgeDefinition | undefined =>
138
+ candidateBadges.find((badge) => badge.handle === handle);
139
+
135
140
  export const getCandidateTemplateDefinition = (
136
141
  templateId: string,
137
142
  ): WorkflowTemplateDefinition | undefined =>
@@ -33,6 +33,7 @@ export interface WorkflowMermaidPreviewVersion {
33
33
  export interface Workflow {
34
34
  id: string;
35
35
  handle?: string;
36
+ teamId?: string | null;
36
37
  name: string;
37
38
  description?: string;
38
39
  avatarEmoji?: string | null;
@@ -1,6 +1,7 @@
1
1
  import { authFetch } from "@/lib/auth-fetch";
2
2
  import { buildBackendHttpUrl, getBackendBaseUrl } from "@/lib/config";
3
3
  import type {
4
+ ApiTeam,
4
5
  ApiWorkflow,
5
6
  ApiWorkflowPagePayload,
6
7
  ApiWorkflowRun,
@@ -105,11 +106,19 @@ export const fetchWorkflowPageData = async (
105
106
 
106
107
  export const fetchPublicWorkflow = async (
107
108
  workflowId: string,
109
+ context?: { workspaceSlug?: string; teamSlug?: string },
108
110
  ): Promise<PublicWorkflowMetadata | undefined> => {
109
111
  try {
110
- return await request<PublicWorkflowMetadata>(
111
- `${PUBLIC_WORKFLOW_PATH}/${workflowId}/public`,
112
- );
112
+ const params = new URLSearchParams();
113
+ if (context?.workspaceSlug) {
114
+ params.set("workspace_slug", context.workspaceSlug);
115
+ }
116
+ if (context?.teamSlug) {
117
+ params.set("team_slug", context.teamSlug);
118
+ }
119
+ const queryString = params.toString();
120
+ const url = `${PUBLIC_WORKFLOW_PATH}/${workflowId}/public${queryString ? `?${queryString}` : ""}`;
121
+ return await request<PublicWorkflowMetadata>(url);
113
122
  } catch (error) {
114
123
  if (
115
124
  error instanceof ApiRequestError &&
@@ -175,12 +184,34 @@ export interface ApiCandidate {
175
184
  export const fetchCandidates = async (): Promise<ApiCandidate[]> =>
176
185
  request<ApiCandidate[]>("/api/candidates");
177
186
 
187
+ export const fetchTeams = async (): Promise<ApiTeam[]> =>
188
+ request<ApiTeam[]>("/api/teams");
189
+
190
+ export const createTeam = async (input: {
191
+ name: string;
192
+ slug: string;
193
+ }): Promise<ApiTeam> =>
194
+ request<ApiTeam>("/api/teams", {
195
+ method: "POST",
196
+ body: JSON.stringify(input),
197
+ });
198
+
199
+ export const deleteTeam = async (teamId: string): Promise<void> =>
200
+ request<void>(`/api/teams/${teamId}`, {
201
+ method: "DELETE",
202
+ expectJson: false,
203
+ });
204
+
178
205
  export const onboardCandidate = async (
179
206
  candidateId: string,
207
+ teamId?: string,
180
208
  ): Promise<ApiWorkflow> =>
181
209
  request<ApiWorkflow>("/api/candidates/onboard", {
182
210
  method: "POST",
183
- body: JSON.stringify({ id: candidateId }),
211
+ body: JSON.stringify({
212
+ id: candidateId,
213
+ ...(teamId ? { team_id: teamId } : {}),
214
+ }),
184
215
  });
185
216
 
186
217
  export const fetchWorkflowListeners = async (
@@ -135,9 +135,15 @@ export const toFlowEdges = (edges: WorkflowEdge[]): FlowEdge[] =>
135
135
 
136
136
  export const getWorkflowRouteRef = (
137
137
  workflow:
138
- | Pick<ApiWorkflow, "id" | "handle">
139
- | Pick<Workflow, "id" | "handle">,
140
- ): string => workflow.handle ?? workflow.id;
138
+ | Pick<ApiWorkflow, "id" | "handle" | "team_id">
139
+ | Pick<Workflow, "id" | "handle" | "teamId">,
140
+ ): string => {
141
+ const teamId = "team_id" in workflow ? workflow.team_id : workflow.teamId;
142
+ if (teamId) {
143
+ return workflow.id;
144
+ }
145
+ return workflow.handle ?? workflow.id;
146
+ };
141
147
 
142
148
  export const resolveWorkflowVersionMermaidSource = (
143
149
  version:
@@ -351,6 +357,7 @@ export const toStoredWorkflow = (
351
357
  return {
352
358
  id: workflow.id,
353
359
  handle: workflow.handle ?? undefined,
360
+ teamId: workflow.team_id ?? undefined,
354
361
  name: workflow.name,
355
362
  description: workflow.description ?? undefined,
356
363
  avatarEmoji,
@@ -458,8 +458,9 @@ export const deleteWorkflow = async (
458
458
 
459
459
  export const onboardCandidateAsWorkflow = async (
460
460
  candidateId: string,
461
+ teamId?: string,
461
462
  ): Promise<StoredWorkflow> => {
462
- const apiWorkflow = await onboardCandidate(candidateId);
463
+ const apiWorkflow = await onboardCandidate(candidateId, teamId);
463
464
  invalidateWorkflowCache(apiWorkflow.id);
464
465
  const stored = await ensureWorkflow(apiWorkflow.id);
465
466
  if (!stored) {
@@ -6,9 +6,17 @@ import type {
6
6
  import type { WorkflowDiffResult, WorkflowSnapshot } from "./workflow-diff";
7
7
  import type { RJSFSchema } from "@rjsf/utils";
8
8
 
9
+ export interface ApiTeam {
10
+ id: string;
11
+ slug: string;
12
+ name: string;
13
+ is_default: boolean;
14
+ }
15
+
9
16
  export interface ApiWorkflow {
10
17
  id: string;
11
18
  handle?: string | null;
19
+ team_id?: string | null;
12
20
  name: string;
13
21
  slug: string;
14
22
  description: string | null;
@@ -237,6 +245,7 @@ export interface WorkflowVersionRecord {
237
245
 
238
246
  export interface StoredWorkflow extends Workflow {
239
247
  versions: WorkflowVersionRecord[];
248
+ teamId?: string | null;
240
249
  draftAccess?: "personal" | "authenticated" | "workspace";
241
250
  isArchived?: boolean;
242
251
  isPublic?: boolean;
@@ -0,0 +1,137 @@
1
+ import { useEffect, useState } from "react";
2
+ import { Button } from "@/design-system/ui/button";
3
+ import {
4
+ Dialog,
5
+ DialogContent,
6
+ DialogDescription,
7
+ DialogFooter,
8
+ DialogHeader,
9
+ DialogTitle,
10
+ } from "@/design-system/ui/dialog";
11
+ import { Input } from "@/design-system/ui/input";
12
+ import { Label } from "@/design-system/ui/label";
13
+
14
+ interface CreateTeamDialogProps {
15
+ open: boolean;
16
+ onOpenChange: (open: boolean) => void;
17
+ onCreate: (name: string, slug: string) => Promise<void>;
18
+ }
19
+
20
+ const toSlug = (name: string): string =>
21
+ name
22
+ .trim()
23
+ .toLowerCase()
24
+ .replace(/[^a-z0-9_-]+/g, "-")
25
+ .replace(/^-+|-+$/g, "");
26
+
27
+ export const CreateTeamDialog = ({
28
+ open,
29
+ onOpenChange,
30
+ onCreate,
31
+ }: CreateTeamDialogProps) => {
32
+ const [name, setName] = useState("");
33
+ const [slug, setSlug] = useState("");
34
+ const [slugDirty, setSlugDirty] = useState(false);
35
+ const [error, setError] = useState<string | null>(null);
36
+ const [isSubmitting, setIsSubmitting] = useState(false);
37
+
38
+ useEffect(() => {
39
+ if (!open) {
40
+ setName("");
41
+ setSlug("");
42
+ setSlugDirty(false);
43
+ setError(null);
44
+ setIsSubmitting(false);
45
+ }
46
+ }, [open]);
47
+
48
+ const handleNameChange = (value: string) => {
49
+ setName(value);
50
+ if (!slugDirty) {
51
+ setSlug(toSlug(value));
52
+ }
53
+ };
54
+
55
+ const handleSlugChange = (value: string) => {
56
+ setSlugDirty(true);
57
+ setSlug(value.toLowerCase().replace(/[^a-z0-9_-]/g, ""));
58
+ };
59
+
60
+ const handleSubmit = async () => {
61
+ const trimmedName = name.trim();
62
+ const trimmedSlug = slug.trim();
63
+ if (!trimmedName || !trimmedSlug) {
64
+ setError("Both name and slug are required.");
65
+ return;
66
+ }
67
+ setError(null);
68
+ setIsSubmitting(true);
69
+ try {
70
+ await onCreate(trimmedName, trimmedSlug);
71
+ onOpenChange(false);
72
+ } catch (err) {
73
+ setError(
74
+ err instanceof Error ? err.message : "Failed to create team.",
75
+ );
76
+ } finally {
77
+ setIsSubmitting(false);
78
+ }
79
+ };
80
+
81
+ return (
82
+ <Dialog open={open} onOpenChange={onOpenChange}>
83
+ <DialogContent>
84
+ <DialogHeader>
85
+ <DialogTitle>New team</DialogTitle>
86
+ <DialogDescription>
87
+ Create a team to group colleagues under a shared label.
88
+ </DialogDescription>
89
+ </DialogHeader>
90
+
91
+ <div className="flex flex-col gap-4 py-2">
92
+ <div className="flex flex-col gap-1.5">
93
+ <Label htmlFor="team-name">Name</Label>
94
+ <Input
95
+ id="team-name"
96
+ placeholder="e.g. Sales"
97
+ value={name}
98
+ onChange={(e) => handleNameChange(e.target.value)}
99
+ onKeyDown={(e) => {
100
+ if (e.key === "Enter") void handleSubmit();
101
+ }}
102
+ autoFocus
103
+ />
104
+ </div>
105
+ <div className="flex flex-col gap-1.5">
106
+ <Label htmlFor="team-slug">Slug</Label>
107
+ <Input
108
+ id="team-slug"
109
+ placeholder="e.g. sales"
110
+ value={slug}
111
+ onChange={(e) => handleSlugChange(e.target.value)}
112
+ onKeyDown={(e) => {
113
+ if (e.key === "Enter") void handleSubmit();
114
+ }}
115
+ />
116
+ <p className="text-xs text-muted-foreground">
117
+ Used in API references. Lowercase letters, numbers, hyphens, and
118
+ underscores only.
119
+ </p>
120
+ </div>
121
+ {error ? (
122
+ <p className="text-sm text-destructive">{error}</p>
123
+ ) : null}
124
+ </div>
125
+
126
+ <DialogFooter>
127
+ <Button variant="outline" onClick={() => onOpenChange(false)}>
128
+ Cancel
129
+ </Button>
130
+ <Button onClick={() => void handleSubmit()} disabled={isSubmitting}>
131
+ {isSubmitting ? "Creating…" : "Create team"}
132
+ </Button>
133
+ </DialogFooter>
134
+ </DialogContent>
135
+ </Dialog>
136
+ );
137
+ };
@@ -0,0 +1,57 @@
1
+ import { Button } from "@/design-system/ui/button";
2
+ import {
3
+ Dialog,
4
+ DialogContent,
5
+ DialogDescription,
6
+ DialogHeader,
7
+ DialogTitle,
8
+ } from "@/design-system/ui/dialog";
9
+ import { type ApiTeam } from "@features/workflow/lib/workflow-storage-api";
10
+
11
+ interface OnboardTeamDialogProps {
12
+ open: boolean;
13
+ candidateName: string;
14
+ teams: ApiTeam[];
15
+ onSelect: (teamId: string) => void;
16
+ onOpenChange: (open: boolean) => void;
17
+ }
18
+
19
+ /**
20
+ * Prompts which team to onboard a candidate into when the workspace has more
21
+ * than one team.
22
+ */
23
+ export const OnboardTeamDialog = ({
24
+ open,
25
+ candidateName,
26
+ teams,
27
+ onSelect,
28
+ onOpenChange,
29
+ }: OnboardTeamDialogProps) => {
30
+ return (
31
+ <Dialog open={open} onOpenChange={onOpenChange}>
32
+ <DialogContent>
33
+ <DialogHeader>
34
+ <DialogTitle>Choose a team</DialogTitle>
35
+ <DialogDescription>
36
+ Select the team to onboard "{candidateName}" into.
37
+ </DialogDescription>
38
+ </DialogHeader>
39
+ <div className="flex flex-col gap-2">
40
+ {teams.map((team) => (
41
+ <Button
42
+ key={team.id}
43
+ variant="outline"
44
+ className="justify-between"
45
+ onClick={() => onSelect(team.id)}
46
+ >
47
+ <span>{team.name}</span>
48
+ {team.is_default ? (
49
+ <span className="text-xs text-muted-foreground">Default</span>
50
+ ) : null}
51
+ </Button>
52
+ ))}
53
+ </div>
54
+ </DialogContent>
55
+ </Dialog>
56
+ );
57
+ };
@@ -0,0 +1,58 @@
1
+ import { type ReactNode, useState } from "react";
2
+ import { ChevronDown, ChevronRight, Trash2 } from "lucide-react";
3
+ import { Button } from "@/design-system/ui/button";
4
+
5
+ interface TeamSectionProps {
6
+ name: string;
7
+ count: number;
8
+ defaultOpen?: boolean;
9
+ onRemove?: () => void;
10
+ children: ReactNode;
11
+ }
12
+
13
+ /**
14
+ * A vertically-stacked, individually collapsible section grouping the AI
15
+ * colleagues that belong to a single team.
16
+ */
17
+ export const TeamSection = ({
18
+ name,
19
+ count,
20
+ defaultOpen = true,
21
+ onRemove,
22
+ children,
23
+ }: TeamSectionProps) => {
24
+ const [isOpen, setIsOpen] = useState(defaultOpen);
25
+
26
+ return (
27
+ <section className="border-b border-border/60 pb-4 last:border-b-0">
28
+ <div className="group flex items-center py-3">
29
+ <button
30
+ type="button"
31
+ aria-expanded={isOpen}
32
+ onClick={() => setIsOpen((open) => !open)}
33
+ className="flex items-center gap-2 text-left"
34
+ >
35
+ {isOpen ? (
36
+ <ChevronDown className="h-4 w-4 text-muted-foreground" />
37
+ ) : (
38
+ <ChevronRight className="h-4 w-4 text-muted-foreground" />
39
+ )}
40
+ <span className="text-sm font-semibold">{name}</span>
41
+ <span className="text-xs text-muted-foreground">{count}</span>
42
+ </button>
43
+ {onRemove ? (
44
+ <Button
45
+ variant="ghost"
46
+ size="icon"
47
+ className="ml-1 h-7 w-7 opacity-0 group-hover:opacity-100"
48
+ onClick={onRemove}
49
+ aria-label={`Remove team ${name}`}
50
+ >
51
+ <Trash2 className="h-3.5 w-3.5 text-muted-foreground" />
52
+ </Button>
53
+ ) : null}
54
+ </div>
55
+ {isOpen ? children : null}
56
+ </section>
57
+ );
58
+ };