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.
- package/.bumpversion.cfg +1 -1
- package/dist/assets/{arc-CI8iBP22.js → arc-De9dLqyl.js} +1 -1
- package/dist/assets/{architectureDiagram-VXUJARFQ-9lccnHEL.js → architectureDiagram-VXUJARFQ-hEslfmYm.js} +1 -1
- package/dist/assets/{blockDiagram-VD42YOAC-CJPKWPSp.js → blockDiagram-VD42YOAC-CyWSMWJa.js} +1 -1
- package/dist/assets/{c4Diagram-YG6GDRKO-CuW7nex5.js → c4Diagram-YG6GDRKO-CxoTVJ47.js} +1 -1
- package/dist/assets/channel-CzCzGn0f.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-BVQ3rR7o.js → chunk-4BX2VUAB-twftUw9o.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-BTpqJ_MA.js → chunk-55IACEB6-DQZDNQUo.js} +1 -1
- package/dist/assets/{chunk-B4BG7PRW-Duatgdyr.js → chunk-B4BG7PRW-BYGOoNB5.js} +1 -1
- package/dist/assets/{chunk-DI55MBZ5-CxEvlbgl.js → chunk-DI55MBZ5-qyPhcZF3.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-C07lS0oH.js → chunk-FMBD7UC4-BgUDHGEG.js} +1 -1
- package/dist/assets/{chunk-QN33PNHL-C09PhK0w.js → chunk-QN33PNHL-DHlMh8NC.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-Dy3GgrIN.js → chunk-QZHKN3VN-Bfc8IV0O.js} +1 -1
- package/dist/assets/{chunk-TZMSLE5B-9HPWR_AG.js → chunk-TZMSLE5B-BqwdftTm.js} +1 -1
- package/dist/assets/classDiagram-2ON5EDUG-ekNI5d_4.js +1 -0
- package/dist/assets/classDiagram-v2-WZHVMYZB-ekNI5d_4.js +1 -0
- package/dist/assets/clone-DowBu1Kh.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-B9TWyynz.js → cose-bilkent-S5V4N54A-g5agH7fS.js} +1 -1
- package/dist/assets/{dagre-6UL2VRFP-DFuJOuNQ.js → dagre-6UL2VRFP-ajLMaXPP.js} +1 -1
- package/dist/assets/{diagram-PSM6KHXK-3Jj-OXdd.js → diagram-PSM6KHXK-BndVcBhB.js} +1 -1
- package/dist/assets/{diagram-QEK2KX5R-DtAwZnbJ.js → diagram-QEK2KX5R-BFiIYeu7.js} +1 -1
- package/dist/assets/{diagram-S2PKOQOG-atZg3Snz.js → diagram-S2PKOQOG-DXy-A9pn.js} +1 -1
- package/dist/assets/{erDiagram-Q2GNP2WA-BoqqRkgy.js → erDiagram-Q2GNP2WA-D_Bac_58.js} +1 -1
- package/dist/assets/{flowDiagram-NV44I4VS-Bmg68IjU.js → flowDiagram-NV44I4VS-DhEi8qtz.js} +1 -1
- package/dist/assets/{ganttDiagram-JELNMOA3-53TZ4oeR.js → ganttDiagram-JELNMOA3-BtpUvOuc.js} +1 -1
- package/dist/assets/{gitGraphDiagram-V2S2FVAM-D1DddraJ.js → gitGraphDiagram-V2S2FVAM-MBES60cx.js} +1 -1
- package/dist/assets/{graph-Cb10Gpkt.js → graph-BcwvIN-m.js} +1 -1
- package/dist/assets/index-DLYzf0K-.js +775 -0
- package/dist/assets/index-DebsXgfj.css +1 -0
- package/dist/assets/{infoDiagram-HS3SLOUP-DxsHJs_0.js → infoDiagram-HS3SLOUP-vaIFYyC9.js} +1 -1
- package/dist/assets/{isUndefined-D1qU3_L5.js → isUndefined-DVO6xakl.js} +1 -1
- package/dist/assets/{journeyDiagram-XKPGCS4Q-CZDE4S5c.js → journeyDiagram-XKPGCS4Q-D-_q99If.js} +1 -1
- package/dist/assets/{kanban-definition-3W4ZIXB7-pRhzMZ6H.js → kanban-definition-3W4ZIXB7-i_AZcs_J.js} +1 -1
- package/dist/assets/{layout-C0AW3yEL.js → layout-CPJ9nR_z.js} +1 -1
- package/dist/assets/{linear-DobjN4tM.js → linear-KBGpPBiH.js} +1 -1
- package/dist/assets/{min-B3Oh8jmi.js → min-By5f7YdD.js} +1 -1
- package/dist/assets/{mindmap-definition-VGOIOE7T-g-SEeftZ.js → mindmap-definition-VGOIOE7T-DRjtRxFS.js} +1 -1
- package/dist/assets/{pieDiagram-ADFJNKIX-DiwhMRkx.js → pieDiagram-ADFJNKIX-BkkSglWM.js} +1 -1
- package/dist/assets/{quadrantDiagram-AYHSOK5B-BIZMdGTC.js → quadrantDiagram-AYHSOK5B-DY-Ng2qt.js} +1 -1
- package/dist/assets/{requirementDiagram-UZGBJVZJ-D5nzjn-8.js → requirementDiagram-UZGBJVZJ-B5w0etXG.js} +1 -1
- package/dist/assets/{sankeyDiagram-TZEHDZUN-CnCESEvf.js → sankeyDiagram-TZEHDZUN-BWntUp8b.js} +1 -1
- package/dist/assets/{sequenceDiagram-WL72ISMW-DKBzac_O.js → sequenceDiagram-WL72ISMW-Cl77fd7y.js} +1 -1
- package/dist/assets/{stateDiagram-FKZM4ZOC-CcT1-5cM.js → stateDiagram-FKZM4ZOC-B8_DoPW6.js} +1 -1
- package/dist/assets/stateDiagram-v2-4FDKWEC3-DHL7p-md.js +1 -0
- package/dist/assets/{timeline-definition-IT6M3QCI-DhFmWcr3.js → timeline-definition-IT6M3QCI-udvNLTe_.js} +1 -1
- package/dist/assets/{treemap-GDKQZRPO-CtMKB2mb.js → treemap-GDKQZRPO-BSvoMl74.js} +1 -1
- package/dist/assets/{xychartDiagram-PRI3JC2R-OhMt1B4k.js → xychartDiagram-PRI3JC2R-CTdy-FCX.js} +1 -1
- package/dist/index.html +2 -7
- package/index.html +0 -5
- package/package.json +1 -1
- package/src/App.tsx +20 -0
- package/src/features/chatkit/pages/public-chat.tsx +7 -3
- package/src/features/workflow/data/templates/candidate-badges.ts +5 -0
- package/src/features/workflow/data/workflow-types.ts +1 -0
- package/src/features/workflow/lib/workflow-storage-api.ts +35 -4
- package/src/features/workflow/lib/workflow-storage-helpers.ts +10 -3
- package/src/features/workflow/lib/workflow-storage.ts +2 -1
- package/src/features/workflow/lib/workflow-storage.types.ts +9 -0
- package/src/features/workflow/pages/workflow-gallery/create-team-dialog.tsx +137 -0
- package/src/features/workflow/pages/workflow-gallery/onboard-team-dialog.tsx +57 -0
- package/src/features/workflow/pages/workflow-gallery/team-section.tsx +58 -0
- package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery-actions.ts +124 -21
- package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery-state.ts +22 -2
- package/src/features/workflow/pages/workflow-gallery/use-workflow-gallery.ts +2 -0
- package/src/features/workflow/pages/workflow-gallery/workflow-card.tsx +26 -5
- package/src/features/workflow/pages/workflow-gallery/workflow-gallery-tabs.tsx +91 -16
- package/src/features/workflow/pages/workflow-gallery.tsx +45 -1
- package/src/lib/api.ts +13 -1
- package/src/lib/workspace-routing.ts +15 -0
- package/src/main.tsx +7 -0
- package/dist/assets/channel-C-DaAO-p.js +0 -1
- package/dist/assets/classDiagram-2ON5EDUG-B2LNkTFb.js +0 -1
- package/dist/assets/classDiagram-v2-WZHVMYZB-B2LNkTFb.js +0 -1
- package/dist/assets/clone-DcGVOYZT.js +0 -1
- package/dist/assets/index-B0_h2wCA.css +0 -1
- package/dist/assets/index-DmIcay1T.js +0 -775
- package/dist/assets/stateDiagram-v2-4FDKWEC3-Cle1H2JX.js +0 -1
package/dist/assets/{xychartDiagram-PRI3JC2R-OhMt1B4k.js → xychartDiagram-PRI3JC2R-CTdy-FCX.js}
RENAMED
|
@@ -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-
|
|
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-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
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
package/package.json
CHANGED
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<{
|
|
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 =>
|
|
@@ -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
|
-
|
|
111
|
-
|
|
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({
|
|
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 =>
|
|
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
|
+
};
|