@snagset/server 0.1.39 → 0.1.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +79 -2
- package/dist/app.js.map +1 -1
- package/dist/config.generated.js +3 -1
- package/dist/config.generated.js.map +1 -1
- package/dist/db/migrations/0014_password_resets.sql +69 -0
- package/dist/email.js +29 -0
- package/dist/email.js.map +1 -0
- package/dist/instance/settings.js +30 -2
- package/dist/instance/settings.js.map +1 -1
- package/dist/jobs/reaper.js +13 -6
- package/dist/jobs/reaper.js.map +1 -1
- package/dist/orgs.js +42 -12
- package/dist/orgs.js.map +1 -1
- package/dist/routes/assignees.js +2 -1
- package/dist/routes/assignees.js.map +1 -1
- package/dist/routes/instance.js +50 -1
- package/dist/routes/instance.js.map +1 -1
- package/dist/routes/integrations.js +2 -1
- package/dist/routes/integrations.js.map +1 -1
- package/dist/routes/invites.js +3 -3
- package/dist/routes/invites.js.map +1 -1
- package/dist/routes/orgs.js +8 -3
- package/dist/routes/orgs.js.map +1 -1
- package/dist/routes/owner.js +52 -9
- package/dist/routes/owner.js.map +1 -1
- package/dist/routes/passwordReset.js +302 -0
- package/dist/routes/passwordReset.js.map +1 -0
- package/package.json +4 -4
- package/static/dashboard/assets/CompositeItem-B_Ebe9qQ.js +1 -0
- package/static/dashboard/assets/CompositeList-CDAPQTbv.js +1 -0
- package/static/dashboard/assets/{ConfirmDialog-D6hyhISD.js → ConfirmDialog-D1II2uNE.js} +1 -1
- package/static/dashboard/assets/{CopyButton-hyiQh1Zr.js → CopyButton-qbA2ZHWn.js} +1 -1
- package/static/dashboard/assets/{FieldItemContext-Gb3Lo5Ci.js → FieldItemContext-BM_KzVUj.js} +1 -1
- package/static/dashboard/assets/InstallTag-DTpNoMn9.js +1 -0
- package/static/dashboard/assets/InstanceRows-B1z-L92-.js +1 -0
- package/static/dashboard/assets/McpTokens-DE2t0iiS.js +1 -0
- package/static/dashboard/assets/{SaveState-CmcJ4unZ.js → SaveState-QQHqDSgx.js} +1 -1
- package/static/dashboard/assets/_app-B_DiSFIt.js +61 -0
- package/static/dashboard/assets/{_app-Dsd1bbNv.js → _app-CaDt6CPJ.js} +1 -1
- package/static/dashboard/assets/{activity-DG1e6z-2.js → activity-CzEy_J8-.js} +1 -1
- package/static/dashboard/assets/{arrow-left-CWiSdgA-.js → arrow-left-RqdORgcq.js} +1 -1
- package/static/dashboard/assets/avatar-D8S5EME4.js +1 -0
- package/static/dashboard/assets/{badge-DEvpNOzs.js → badge-anHWhPFa.js} +1 -1
- package/static/dashboard/assets/{card--8RLEIGP.js → card-C01exIzZ.js} +1 -1
- package/static/dashboard/assets/{check-XHwuM5zg.js → check-Gm9Xu4rH.js} +1 -1
- package/static/dashboard/assets/chevron-down-C-pVPOKU.js +6 -0
- package/static/dashboard/assets/{chevron-right-CG2rSUns.js → chevron-right-BQmYcugl.js} +1 -1
- package/static/dashboard/assets/{copy-eakuUTpI.js → copy-Ckk__4oN.js} +1 -1
- package/static/dashboard/assets/{dialog-D0b9dX2m.js → dialog-D5G50pSW.js} +2 -2
- package/static/dashboard/assets/dropdown-menu-Bm_opJ2p.js +11 -0
- package/static/dashboard/assets/forgot-R7JqXxs5.js +1 -0
- package/static/dashboard/assets/{image-off-DZOyJVTM.js → image-off-BtbHLgOM.js} +1 -1
- package/static/dashboard/assets/index-CoiIx5MR.css +1 -0
- package/static/dashboard/assets/index-p-qD6BlY.js +162 -0
- package/static/dashboard/assets/invite-9eotNL9V.js +1 -0
- package/static/dashboard/assets/label-CuvYMIVd.js +1 -0
- package/static/dashboard/assets/{labels-CUVq_Uf0.js → labels-DLMjYGNJ.js} +1 -1
- package/static/dashboard/assets/{layout-grid-BXWirugp.js → layout-grid-D5ek58h7.js} +1 -1
- package/static/dashboard/assets/{parts-B46j7QJi.js → parts-CN1uQywE.js} +1 -1
- package/static/dashboard/assets/{plus-yZPLf0r6.js → plus-BsxOLtMA.js} +1 -1
- package/static/dashboard/assets/reset-8K76tpdp.js +1 -0
- package/static/dashboard/assets/s._site-egkPDOj9.js +1 -0
- package/static/dashboard/assets/s._site.comments-DmrtKLHb.js +49 -0
- package/static/dashboard/assets/s._site.reviewers-CGKj-NQo.js +1 -0
- package/static/dashboard/assets/s._site.settings-DvWbfMac.js +1 -0
- package/static/dashboard/assets/serializeValue-DRMAHja_.js +1 -0
- package/static/dashboard/assets/{setup-CefvYWGy.js → setup-BDZJmgIB.js} +1 -1
- package/static/dashboard/assets/sign-in-BmweKwDv.js +1 -0
- package/static/dashboard/assets/{sign-up-nsUll0Gy.js → sign-up-C06mQl-c.js} +1 -1
- package/static/dashboard/assets/sites-CkGgns1o.js +1 -0
- package/static/dashboard/assets/studio-BHpeuJSq.js +1 -0
- package/static/dashboard/assets/studio-CG1QMBSK.js +1 -0
- package/static/dashboard/assets/studio.index-BOkmM7I5.js +1 -0
- package/static/dashboard/assets/studio.integrations-BBik3DdK.js +1 -0
- package/static/dashboard/assets/studio.members-CEKrbJHp.js +11 -0
- package/static/dashboard/assets/studio.plan-CkOzUaJM.js +1 -0
- package/static/dashboard/assets/{sun-Djjj3CUF.js → sun-DXwtJyi5.js} +1 -1
- package/static/dashboard/assets/{switch-WMRF6OK1.js → switch-cgJL6ATv.js} +1 -1
- package/static/dashboard/assets/{table-CgakQRAC.js → table-B8VTYpZO.js} +1 -1
- package/static/dashboard/assets/{useAriaLabelledBy-obWCIEg1.js → useAriaLabelledBy-BT5NtL2E.js} +1 -1
- package/static/dashboard/assets/{useBaseQuery-DG87f6yY.js → useBaseQuery-DaFWLbWu.js} +1 -1
- package/static/dashboard/assets/useQuery-D-9vXtOo.js +1 -0
- package/static/dashboard/assets/useRender-BBzDGtxq.js +1 -0
- package/static/dashboard/assets/{useValueChanged-CHzVruZ0.js → useValueChanged-cwqHWzY1.js} +1 -1
- package/static/dashboard/assets/{users-BvfczmcS.js → users-CGY0g3mg.js} +1 -1
- package/static/dashboard/assets/{welcome-D5mKF-J3.js → welcome-CNtwDQMd.js} +3 -3
- package/static/dashboard/assets/{widget-signin-BmCBYJgF.js → widget-signin-BOUMPGXt.js} +1 -1
- package/static/dashboard/assets/you-DH4QMiND.js +1 -0
- package/static/dashboard/assets/{zod-D7wsVto4.js → zod-DfHlfdUJ.js} +1 -1
- package/static/dashboard/index.html +2 -2
- package/static/widget/build.json +2 -2
- package/static/widget/snag.js +1 -1
- package/static/widget/snag.runtime.js +1 -1
- package/static/dashboard/assets/CompositeItem-Yi3wWYM7.js +0 -1
- package/static/dashboard/assets/InstallTag-BRvgfS91.js +0 -1
- package/static/dashboard/assets/McpTokens--C1TeZqq.js +0 -1
- package/static/dashboard/assets/_app-CBnYJfLp.js +0 -56
- package/static/dashboard/assets/avatar-nrKPMD7A.js +0 -1
- package/static/dashboard/assets/dropdown-menu-BMIst4Wj.js +0 -11
- package/static/dashboard/assets/index-COORBw3J.js +0 -169
- package/static/dashboard/assets/index-CpG77B7f.css +0 -1
- package/static/dashboard/assets/invite-Df7qdHBL.js +0 -1
- package/static/dashboard/assets/label-Ds2CYKth.js +0 -1
- package/static/dashboard/assets/s._site-BQiiE9w4.js +0 -1
- package/static/dashboard/assets/s._site.comments-CQYkPWo4.js +0 -54
- package/static/dashboard/assets/s._site.reviewers-BCQWB5GA.js +0 -1
- package/static/dashboard/assets/s._site.settings-D9Qix9dj.js +0 -1
- package/static/dashboard/assets/sign-in-Q4AWMmvk.js +0 -1
- package/static/dashboard/assets/sites-BKsg9Pla.js +0 -1
- package/static/dashboard/assets/studio-CSGo60dt.js +0 -1
- package/static/dashboard/assets/studio-ytRb_WCk.js +0 -1
- package/static/dashboard/assets/studio.index-Cul4Sj15.js +0 -1
- package/static/dashboard/assets/studio.integrations-CC61jGXL.js +0 -1
- package/static/dashboard/assets/studio.members-DEM1MjLy.js +0 -6
- package/static/dashboard/assets/useQuery-DT3w11tR.js +0 -1
- package/static/dashboard/assets/useRender-Bu6Vyu1g.js +0 -1
- package/static/dashboard/assets/you-Dsp9kUVS.js +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{d as B,r as h,_ as v,s as y,$ as b,Z as C,j as e,B as g,z as S,C as K,q as M,S as G}from"./index-COORBw3J.js";import{u as w}from"./useQuery-DT3w11tR.js";import{L as A,I as k}from"./label-Ds2CYKth.js";import{E as T,G as I,I as U,H as z,J as L,K as _}from"./dialog-D0b9dX2m.js";import{C as O}from"./ConfirmDialog-D6hyhISD.js";import{a as D,A as q,P as H}from"./parts-B46j7QJi.js";import{u as F,b as E}from"./labels-CUVq_Uf0.js";import{S as Q}from"./SaveState-CmcJ4unZ.js";import{b as V}from"./studio-CSGo60dt.js";import{c as W}from"./update-duXzIzFS.js";import"./useBaseQuery-DG87f6yY.js";import"./card--8RLEIGP.js";function J({owner:n}){const s=w(S.instance()),i=B(),t=()=>i.invalidateQueries({queryKey:S.instance().queryKey}),[c,l]=h.useState(!1),[p,x]=h.useState(!1),[d,u]=h.useState(null),a=v({mutationFn:y.disconnectStorage,onSuccess:()=>void t(),onError:j=>b.error(C(j))}),r=v({mutationFn:y.disconnectMail,onSuccess:()=>void t(),onError:j=>b.error(C(j))}),f=v({mutationFn:y.testMail,onSuccess:j=>b.success(`Sent to ${j.to}`),onError:j=>b.error(C(j))}),N=F(),m=s.data?.storage,o=s.data?.mail;return e.jsxs(e.Fragment,{children:[e.jsxs(D,{title:"Storage",description:"The bucket screenshots and files go to. Without one the widget runs in annotation mode.",children:[!s.data&&e.jsx("p",{className:"m-0 text-[13px] text-muted-foreground",children:s.isError?"Could not read it.":"Reading…"}),m&&e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx(P,{tone:m.connected?"ok":m.unreadable?"bad":"off",text:m.connected?`Connected · ${m.bucket} at ${(m.endpoint??"").replace(/^https?:\/\//,"")}${m.connectedAt?` · since ${E(m.connectedAt)}`:""}`:m.unreadable?"A bucket is stored but its keys will not decrypt — the secret key changed. Connect it again.":"Not connected — annotation mode. Comments and anchors work; screenshots are not offered."}),N.limits.storage.limit!==null&&e.jsxs("p",{className:"m-0 text-[12px] text-muted-foreground",children:[$(N.limits.storage.used)," of ",$(N.limits.storage.limit)," used across the studio's sites."]}),m.source==="env"?e.jsx(R,{what:"SNAGSET_STORAGE_S3_*"}):n?e.jsxs("div",{className:"flex gap-2",children:[e.jsx(g,{variant:"outline",size:"sm",onClick:()=>l(!0),children:m.connected||m.unreadable?"Replace…":"Connect a bucket…"}),(m.connected||m.unreadable)&&e.jsx(g,{variant:"ghost",size:"sm",onClick:()=>u("storage"),children:"Disconnect"})]}):null]})]}),e.jsx(D,{title:"Mail",description:"How notifications go out. Without a transport they are off, and the instance says so.",children:o&&e.jsxs("div",{className:"flex flex-col gap-2",children:[e.jsx(P,{tone:o.configured?"ok":o.unreadable?"bad":"off",text:o.configured?`${o.scheme==="resend"?"Resend":`SMTP · ${o.host??""}`} · from ${o.from}${o.updatedAt?` · since ${E(o.updatedAt)}`:""}`:o.unreadable?"A transport is stored but its secret will not decrypt — the secret key changed. Connect it again.":"No transport — notifications are off."}),o.source==="env"?e.jsx(R,{what:"SNAGSET_MAIL_URL"}):n?e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx(g,{variant:"outline",size:"sm",onClick:()=>x(!0),children:o.configured||o.unreadable?"Replace…":"Connect a transport…"}),o.configured&&e.jsx(g,{variant:"outline",size:"sm",loading:f.isPending,onClick:()=>f.mutate(),children:f.isPending?"Sending…":"Send a test email"}),(o.configured||o.unreadable)&&e.jsx(g,{variant:"ghost",size:"sm",onClick:()=>u("mail"),children:"Disconnect"})]}):(o.configured&&n===!1,null)]})}),e.jsx(Z,{open:c,onOpenChange:l,onDone:()=>void t()}),e.jsx(X,{open:p,onOpenChange:x,onDone:()=>void t()}),e.jsx(O,{open:d!==null,onOpenChange:j=>!j&&u(null),title:d==="storage"?"Disconnect the bucket?":"Disconnect the mail transport?",description:d==="storage"?"Screenshots stop being offered. Nothing in the bucket is deleted, and reconnecting the same bucket brings the pictures back.":"Notifications stop going out until a transport is connected again.",confirmLabel:"Disconnect",busy:a.isPending||r.isPending,onConfirm:async()=>{d==="storage"?await a.mutateAsync():await r.mutateAsync(),u(null)}})]})}function P({tone:n,text:s}){return e.jsxs("p",{className:"m-0 flex items-center gap-2 text-[13px]",children:[e.jsx("span",{className:`size-2 shrink-0 rounded-full ${n==="ok"?"bg-ok":n==="bad"?"bg-destructive":"bg-line-strong"}`,"aria-hidden":"true"}),e.jsx("span",{children:s})]})}function R({what:n}){return e.jsxs("p",{className:"m-0 text-[12px] text-muted-foreground",children:["Set by the environment (",e.jsx("code",{className:"font-mono",children:n}),"). Change it there; the dashboard shows it and does not offer to edit it."]})}function Z({open:n,onOpenChange:s,onDone:i}){const[t,c]=h.useState({endpoint:"",bucket:"",region:"",accessKeyId:"",secretAccessKey:"",publicBaseUrl:""}),[l,p]=h.useState(""),x=v({mutationFn:y.connectStorage}),d=r=>f=>c({...t,[r]:f.target.value}),u=async r=>{r.preventDefault(),p("");try{await x.mutateAsync({endpoint:t.endpoint.trim(),bucket:t.bucket.trim(),accessKeyId:t.accessKeyId.trim(),secretAccessKey:t.secretAccessKey,...t.region.trim()?{region:t.region.trim()}:{},...t.publicBaseUrl.trim()?{publicBaseUrl:t.publicBaseUrl.trim()}:{}}),c({endpoint:"",bucket:"",region:"",accessKeyId:"",secretAccessKey:"",publicBaseUrl:""}),s(!1),i(),b.success("Bucket connected — screenshots are on")}catch(f){p(f instanceof K?f.message:"Could not reach the server.")}},a=(r,f,N,m="text",o)=>e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(A,{htmlFor:`st-${r}`,children:f}),e.jsx(k,{id:`st-${r}`,type:m,placeholder:N,value:t[r],onChange:d(r),autoComplete:"off"}),o&&e.jsx("p",{className:"m-0 text-[12px] text-muted-foreground",children:o})]});return e.jsx(T,{open:n,onOpenChange:s,children:e.jsx(I,{children:e.jsxs("form",{onSubmit:r=>void u(r),className:"flex flex-col gap-4",noValidate:!0,children:[e.jsxs(U,{children:[e.jsx(z,{children:"Connect a bucket"}),e.jsx(L,{children:"Any S3-compatible bucket. It is probed — write, read, delete — before anything is saved, and the keys are stored encrypted with this instance's secret key."})]}),l&&e.jsx("p",{role:"alert",className:"m-0 rounded-md border border-destructive/40 bg-warn-soft px-3 py-2 text-[13px] text-destructive",children:l}),a("endpoint","Endpoint","https://ams3.digitaloceanspaces.com"),a("bucket","Bucket","snagset"),a("region","Region","auto","text","Leave empty for providers that do not use one."),a("accessKeyId","Access key ID",""),a("secretAccessKey","Secret access key","","password"),a("publicBaseUrl","Public base URL","","text","Optional — only if the bucket is served from a CDN address."),e.jsxs(_,{children:[e.jsx(g,{type:"button",variant:"outline",onClick:()=>s(!1),children:"Cancel"}),e.jsx(g,{type:"submit",loading:x.isPending,children:x.isPending?"Probing…":"Connect"})]})]})})})}function X({open:n,onOpenChange:s,onDone:i}){const[t,c]=h.useState(""),[l,p]=h.useState(""),[x,d]=h.useState(""),u=v({mutationFn:y.connectMail}),a=async r=>{r.preventDefault(),d("");try{await u.mutateAsync({url:t.trim(),from:l.trim()}),c(""),p(""),s(!1),i(),b.success("Transport connected — send a test email to be sure")}catch(f){d(f instanceof K?f.message:"Could not reach the server.")}};return e.jsx(T,{open:n,onOpenChange:s,children:e.jsx(I,{children:e.jsxs("form",{onSubmit:r=>void a(r),className:"flex flex-col gap-4",noValidate:!0,children:[e.jsxs(U,{children:[e.jsx(z,{children:"Connect a mail transport"}),e.jsx(L,{children:"Resend is the easy one — the provider owns SPF and DKIM. SMTP works with any provider. The URL is stored encrypted with this instance's secret key."})]}),x&&e.jsx("p",{role:"alert",className:"m-0 rounded-md border border-destructive/40 bg-warn-soft px-3 py-2 text-[13px] text-destructive",children:x}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(A,{htmlFor:"mail-url",children:"Transport"}),e.jsx(k,{id:"mail-url",type:"password",placeholder:"resend://re_… or smtp://user:pass@host:587",value:t,onChange:r=>c(r.target.value),autoComplete:"off"})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(A,{htmlFor:"mail-from",children:"From"}),e.jsx(k,{id:"mail-from",placeholder:"Snagset <review@kilnwork.studio>",value:l,onChange:r=>p(r.target.value)}),e.jsx("p",{className:"m-0 text-[12px] text-muted-foreground",children:"The address every notification is sent from. Resend needs a verified domain for it."})]}),e.jsxs(_,{children:[e.jsx(g,{type:"button",variant:"outline",onClick:()=>s(!1),children:"Cancel"}),e.jsx(g,{type:"submit",loading:u.isPending,disabled:!t.trim()||!l.trim(),children:u.isPending?"Saving…":"Connect"})]})]})})})}function $(n){const s=["B","KB","MB","GB","TB"];let i=n,t=0;for(;i>=1024&&t<s.length-1;)i/=1024,t+=1;return`${t===0?i:i.toFixed(i>=10?0:1)} ${s[t]}`}function Y(){const n=F(),s=w(S.meta()),i=w(S.members()),t=V(),c=M(n.user.role,"studio"),[l,p]=h.useState(n.org.name),[x,d]=h.useState(null);h.useEffect(()=>p(n.org.name),[n.org.name]);const u=c&&l.trim().length>0&&l.trim()!==n.org.name;return e.jsxs("div",{className:"flex flex-col gap-10 p-7",children:[e.jsx(H,{title:"General",sub:n.org.name,actions:e.jsx(Q,{saving:t.isPending,savedAt:x})}),e.jsx(D,{title:"Studio",description:"The name at the top of every screen.",children:e.jsxs("form",{className:"flex flex-col gap-1.5",onSubmit:a=>{a.preventDefault(),u&&t.mutate(l.trim(),{onSuccess:()=>d(Date.now())})},children:[e.jsx(A,{htmlFor:"studio-name",children:"Name"}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(k,{id:"studio-name",value:l,readOnly:!c,onChange:a=>p(a.target.value)}),u&&e.jsx(g,{type:"submit",variant:"outline",children:"Save"})]}),e.jsx("p",{className:"m-0 text-[12px] text-muted-foreground",children:c?"Shown at the top of the sidebar, above every site.":"Only an owner can change it."})]})}),e.jsx(D,{title:"This instance",description:"What is running, and where.",children:e.jsxs("dl",{className:"m-0 grid grid-cols-[130px_1fr] gap-x-3 gap-y-1.5 text-[13px]",children:[e.jsx("dt",{className:"text-muted-foreground",children:"Version"}),e.jsxs("dd",{className:"m-0",children:[s.data?.version??"—"," ",e.jsx(ee,{version:s.data?.version??""})]}),e.jsx("dt",{className:"text-muted-foreground",children:"Address"}),e.jsx("dd",{className:"m-0",children:e.jsx(te,{owner:c})}),s.data?.commit&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-muted-foreground",children:"Commit"}),e.jsxs("dd",{className:"m-0 font-mono text-[12px] text-ink-soft",children:[s.data.commit.slice(0,12),s.data.modified?" · modified":""]})]}),e.jsx("dt",{className:"text-muted-foreground",children:"Sites · members"}),e.jsxs("dd",{className:"m-0",children:[n.projects.length," · ",i.data?.data.length??"—"]})]})}),e.jsx(J,{owner:c})]})}function ee({version:n}){const[s,i]=h.useState({s:"idle"});return s.s==="idle"?e.jsxs(e.Fragment,{children:["·"," ",e.jsx("button",{type:"button",className:"text-brand hover:underline",onClick:()=>{i({s:"checking"}),W(n).then(t=>i({s:"done",r:t})).catch(()=>i({s:"failed"}))},children:"Check for updates"})," ",e.jsx("span",{className:"text-[12px] text-muted-foreground",children:"— from your browser, never from the server"})]}):s.s==="checking"?e.jsxs("span",{className:"inline-flex items-center gap-1 text-muted-foreground",children:["· ",e.jsx(G,{className:"size-3"})," checking…"]}):s.s==="failed"?e.jsx("span",{className:"text-muted-foreground",children:"· could not reach npm"}):e.jsx("span",{className:"text-muted-foreground",children:s.r?.newer?`· ${s.r.latest} is out`:"· up to date"})}function te({owner:n}){const s=B(),i=w(S.instance()),[t,c]=h.useState(null),[l,p]=h.useState(""),x=v({mutationFn:a=>y.setAddress(a),onSuccess:a=>{s.setQueryData(S.instance().queryKey,a),c(null),p(""),b.success("Address saved — every new link is built from it")},onError:a=>p(C(a))}),d=i.data?.address;if(!d)return e.jsx("span",{className:"font-mono text-[12px] text-ink-soft",children:location.origin});const u=d.source==="env";return t===null||!n||u?e.jsxs("span",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("span",{className:"font-mono text-[12px] text-ink-soft",children:d.url}),e.jsx("span",{className:"text-[12px] text-muted-foreground",children:u?"· set by the environment (SNAGSET_BASE_URL)":d.source==="stored"?"· set at claim; every link is built from it":"· this machine's default"}),n&&!u&&e.jsx(g,{variant:"ghost",size:"sm",onClick:()=>c(d.url),children:"Change…"})]}):e.jsxs("form",{className:"flex flex-col gap-1.5",onSubmit:a=>{a.preventDefault(),x.mutate(t.trim())},children:[e.jsxs("span",{className:"flex items-center gap-2",children:[e.jsx(k,{"aria-label":"Address",value:t,onChange:a=>c(a.target.value),className:"h-8 max-w-[360px] font-mono text-[12px]",autoFocus:!0}),e.jsx(g,{type:"submit",size:"sm",loading:x.isPending,disabled:!t.trim(),children:x.isPending?"Saving…":"Save"}),e.jsx(g,{type:"button",variant:"ghost",size:"sm",onClick:()=>c(null),children:"Cancel"})]}),l&&e.jsx("span",{className:"text-[12px] text-destructive",children:l}),e.jsx("span",{className:"text-[12px] text-muted-foreground",children:"https:// unless it is this machine; the origin only. Review links, issues and emails are built from it."})]})}const he=()=>M(F().user.role,"settings")?e.jsx(Y,{}):e.jsx(q,{title:"General"});export{he as component};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{q as U,j as e,L as ee,d as te,r as a,_ as F,$ as I,Z as M,s as T,a0 as D,W as se,B as y,C as K,z as ne}from"./index-COORBw3J.js";import{u as B}from"./useQuery-DT3w11tR.js";import{B as _}from"./badge-DEvpNOzs.js";import{E,G as O,I as W,H as R,J,K as H}from"./dialog-D0b9dX2m.js";import{L as C,I as A}from"./label-Ds2CYKth.js";import{C as oe}from"./ConfirmDialog-D6hyhISD.js";import{A as ae,P as re,a as ie}from"./parts-B46j7QJi.js";import{u as q,b as ce}from"./labels-CUVq_Uf0.js";import{C as z}from"./CopyButton-hyiQh1Zr.js";import{M as le}from"./McpTokens--C1TeZqq.js";import"./useBaseQuery-DG87f6yY.js";import"./useRender-Bu6Vyu1g.js";import"./card--8RLEIGP.js";import"./check-XHwuM5zg.js";import"./copy-eakuUTpI.js";import"./switch-WMRF6OK1.js";import"./useAriaLabelledBy-obWCIEg1.js";import"./useValueChanged-CHzVruZ0.js";const de={ok:{label:"Healthy",tone:"ok"},needs_reauth:{label:"Needs a new token",tone:"bad"},error:{label:"Failing",tone:"bad"}};function he(){const o=q(),i=U(o.user.role,"settings"),x=U(o.user.role,"studio"),f=B(ne.integrations()),S=te(),w=()=>S.invalidateQueries({queryKey:["integrations"]}),[l,u]=a.useState(null),[r,g]=a.useState(null),[j,m]=a.useState(null),b=F({mutationFn:t=>T.regenerateGitHubWebhook(t),onSuccess:({webhook:t},s)=>m({site:s,provider:"github",hook:t,fresh:!1}),onError:t=>I.error(M(t))}),N=F({mutationFn:({uid:t,provider:s})=>T.testTracker(t,s).then(c=>({...c,provider:s})),onSuccess:t=>{I[t.ok?"success":"error"](t.ok?`${D[t.provider]} can see ${t.target}`:t.detail??`${D[t.provider]} refused the token`),w()},onError:t=>I.error(M(t))}),k=F({mutationFn:({uid:t,provider:s})=>T.disconnectTracker(t,s),onSuccess:()=>{g(null),w()},onError:t=>I.error(M(t))}),v=f.data?.data??[],h=t=>v.filter(s=>s.provider===t),d=t=>o.projects.filter(s=>!h(t).some(c=>c.site===s.uid));return e.jsxs("div",{className:"flex flex-col gap-10 p-7",children:[e.jsx(re,{title:"Integrations",sub:"Mirrors converge on the comment's status here; Slack only announces"}),["github","linear","jira","slack"].map(t=>{const s=h(t),c=d(t);return e.jsxs(ie,{title:D[t],description:t==="github"?"A comment becomes an issue in the site's repository, with the brief as its body; resolving it here closes the issue there, and closing it there resolves it here. A fine-grained token per site — no OAuth app to register.":t==="linear"?"A comment becomes an issue in the site's team, with the brief as its body; our status moves it between the team's workflow states by type, and a webhook from Linear moves it back. A personal API key per site — no OAuth app to register.":t==="jira"?"A comment becomes an issue in the site's Jira project, with the brief as its description; our status moves it through the workflow by status category, and a webhook from Jira moves it back. An API token with the account's email — what Atlassian gives a self-hoster.":"One message per comment in a channel, edited in place as the status moves — never a second message, never a reply. A bot token from an app you create in a click from our manifest; Slack sends nothing back.",children:[f.isPending&&e.jsx(se,{rows:2}),f.isSuccess&&s.length===0&&e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx(_,{variant:"outline",children:"Not connected"}),e.jsx("span",{className:"text-[13px] text-muted-foreground",children:t==="github"?"No site is connected to a repository yet.":t==="linear"?"No site is connected to a team yet.":t==="jira"?"No site is connected to a project yet.":"No site is connected to a channel yet."})]}),s.length>0&&e.jsx("ul",{className:"m-0 flex list-none flex-col gap-2 p-0",children:s.map(n=>{const P=de[n.health]??{label:n.health,tone:"warn"};return e.jsxs("li",{className:"flex flex-wrap items-center gap-x-3 gap-y-1 rounded-md border border-border px-3 py-2.5 text-[13px]",children:[e.jsx("span",{className:"font-medium",children:n.siteName??n.site}),e.jsx("span",{className:"text-muted-foreground",children:"→"}),e.jsx("code",{className:"font-mono text-[12.5px]",children:n.provider==="github"?`${n.owner}/${n.repo}`:n.provider==="linear"?`${n.teamKey} · ${n.teamName}`:n.provider==="jira"?`${n.projectKey} · ${n.projectName}`:`#${n.channelName} · ${n.teamName}`}),e.jsx(_,{variant:"outline",className:P.tone==="ok"?"border-ok text-ok":P.tone==="bad"?"border-destructive text-destructive":"",children:P.label}),e.jsxs("span",{className:"text-[12px] text-muted-foreground",children:[n.provider==="slack"?n.createOn==="thread.created"?"A message for every new comment":"Messages when you ask, from the brief":n.createOn==="thread.created"?"An issue for every new comment":"Issues when you ask, from the brief",n.provider==="github"&&n.labels.length?` · labels ${n.labels.join(", ")}`:"",n.provider!=="github"&&n.provider!=="slack"&&!n.hasWebhookSecret?` · no webhook secret yet, so ${D[n.provider]} cannot move comments here`:""," · ",ce(n.updatedAt)]}),n.lastError&&e.jsx("span",{className:"basis-full text-[12px] text-destructive",children:n.lastError}),i&&e.jsxs("span",{className:"ml-auto flex gap-1",children:[n.provider==="github"?e.jsx(y,{variant:"ghost",size:"sm",loading:b.isPending,onClick:()=>b.mutate(n.site),children:"Webhook…"}):n.provider==="slack"?null:e.jsx(y,{variant:"ghost",size:"sm",onClick:()=>m({site:n.site,provider:n.provider,hook:{url:`${location.origin}/api/v1/integrations/${n.provider}/inbound`},fresh:!1}),children:"Webhook…"}),e.jsx(y,{variant:"ghost",size:"sm",loading:N.isPending,onClick:()=>N.mutate({uid:n.site,provider:n.provider}),children:"Test"}),e.jsx(y,{variant:"ghost",size:"sm",onClick:()=>g(n),children:"Disconnect"})]})]},`${n.provider}:${n.site}`)})}),i&&c.length>0&&e.jsx("div",{children:e.jsx(y,{variant:"outline",size:"sm",onClick:()=>u(t),children:"Connect a site…"})})]},t)}),e.jsx(le,{owner:i,instanceOwner:x,sites:o.projects}),e.jsx(me,{open:l==="github",onOpenChange:t=>!t&&u(null),sites:d("github"),onDone:(t,s)=>{w(),s&&m({site:t,provider:"github",hook:s,fresh:!0})}}),e.jsx(pe,{open:l==="linear",onOpenChange:t=>!t&&u(null),sites:d("linear"),onDone:(t,s)=>{w(),m({site:t,provider:"linear",hook:s,fresh:!0})}}),e.jsx(xe,{open:l==="jira",onOpenChange:t=>!t&&u(null),sites:d("jira"),onDone:(t,s)=>{w(),m({site:t,provider:"jira",hook:s,fresh:!0})}}),e.jsx(fe,{open:l==="slack",onOpenChange:t=>!t&&u(null),sites:d("slack"),onDone:()=>void w()}),e.jsx(ue,{state:j,onClose:()=>m(null),siteName:t=>v.find(s=>s.site===t)?.siteName??"the site"}),e.jsx(oe,{open:r!==null,onOpenChange:t=>!t&&g(null),title:`Disconnect ${r?.siteName??"this site"} from ${r?D[r.provider]:"the tracker"}?`,description:r?.provider==="slack"?e.jsx(e.Fragment,{children:"Messages already posted stay in the channel; no new ones are posted and they stop being updated. The token is forgotten."}):e.jsx(e.Fragment,{children:"Issues already made keep their links; no new ones are made and status stops mirroring. The token is forgotten."}),confirmLabel:"Disconnect",busy:k.isPending,onConfirm:async()=>{r&&await k.mutateAsync({uid:r.site,provider:r.provider})}})]})}function me({open:o,onOpenChange:i,sites:x,onDone:f}){const[S,w]=a.useState(""),[l,u]=a.useState(""),[r,g]=a.useState(""),[j,m]=a.useState(""),[b,N]=a.useState(!1),[k,v]=a.useState(""),h=F({mutationFn:s=>T.connectTracker(s,"github",{repository:l.trim(),token:r.trim(),labels:j,createOn:b?"thread.created":"manual"})}),d=S||x[0]?.uid||"",t=async s=>{s.preventDefault(),v("");try{const c=await h.mutateAsync(d);u(""),g(""),m(""),N(!1),i(!1),f(d,c.webhook&&"secret"in c.webhook?c.webhook:void 0),I.success("GitHub connected")}catch(c){v(c instanceof K?c.message:"Could not reach the server.")}};return e.jsx(E,{open:o,onOpenChange:i,children:e.jsx(O,{children:e.jsxs("form",{onSubmit:s=>void t(s),className:"flex flex-col gap-4",noValidate:!0,children:[e.jsxs(W,{children:[e.jsx(R,{children:"Connect a site to GitHub"}),e.jsxs(J,{children:["A fine-grained personal access token with ",e.jsx("b",{children:"Issues: read and write"})," on that one repository. It is checked against the repository before it is saved, and stored encrypted with this instance's secret key."]})]}),k&&e.jsx("p",{role:"alert",className:"m-0 rounded-md border border-destructive/40 bg-warn-soft px-3 py-2 text-[13px] text-destructive",children:k}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(C,{htmlFor:"gh-site",children:"Site"}),e.jsx("select",{id:"gh-site",value:d,onChange:s=>w(s.target.value),className:"h-8 rounded-md border border-input bg-background px-2.5 text-[13px]",children:x.map(s=>e.jsx("option",{value:s.uid,children:s.name},s.uid))})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(C,{htmlFor:"gh-repo",children:"Repository"}),e.jsx(A,{id:"gh-repo",placeholder:"kilnwork/site",value:l,onChange:s=>u(s.target.value),autoComplete:"off"})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(C,{htmlFor:"gh-token",children:"Token"}),e.jsx(A,{id:"gh-token",type:"password",placeholder:"github_pat_…",value:r,onChange:s=>g(s.target.value),autoComplete:"off"})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(C,{htmlFor:"gh-labels",children:"Labels"}),e.jsx(A,{id:"gh-labels",placeholder:"review, bug",value:j,onChange:s=>m(s.target.value)}),e.jsx("p",{className:"m-0 text-[12px] text-muted-foreground",children:"Applied to every issue this makes. Optional."})]}),e.jsxs("label",{className:"flex cursor-pointer items-start gap-2.5 text-[13px]",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:b,onChange:s=>N(s.target.checked)}),e.jsxs("span",{children:[e.jsx("span",{className:"block font-medium",children:"An issue for every new comment"}),e.jsx("span",{className:"block text-[12px] text-muted-foreground",children:'Off by default: a reviewer walking a site leaves twenty comments and half are "never mind". Make issues from the brief when you mean it.'})]})]}),e.jsxs(H,{children:[e.jsx(y,{type:"button",variant:"outline",onClick:()=>i(!1),children:"Cancel"}),e.jsx(y,{type:"submit",loading:h.isPending,disabled:!l.trim()||!r.trim()||!d,children:h.isPending?"Checking…":"Connect"})]})]})})})}function ue({state:o,onClose:i,siteName:x}){const f=o&&"secret"in o.hook?o.hook.secret:null;return e.jsx(E,{open:o!==null,onOpenChange:S=>!S&&i(),children:e.jsxs(O,{children:[e.jsxs(W,{children:[e.jsx(R,{children:o?.fresh?"Connected — now the webhook":"Webhook for "+(o?x(o.site):"")}),e.jsx(J,{children:o?.provider==="linear"?"Issues moved in Linear move the comment here. In Linear › Settings › API › Webhooks, create one for the team with this URL and Issue events; then paste its signing secret when connecting or replacing the connection.":o?.provider==="jira"?"Issues moved in Jira move the comment here. In Jira › Settings › System › Webhooks, create one with this URL, the events Issue updated and Issue deleted, and a secret; then paste the same secret when connecting or replacing the connection.":`Issues closed or reopened on GitHub move the comment here. Add a webhook to the repository — Settings › Webhooks › Add webhook — with these values. The secret is shown this once${o?.fresh?"":", and the previous one has stopped working"}.`})]}),o&&e.jsxs("dl",{className:"m-0 grid grid-cols-[110px_1fr] gap-x-3 gap-y-2 text-[13px]",children:[e.jsx("dt",{className:"text-muted-foreground",children:"Payload URL"}),e.jsxs("dd",{className:"m-0 flex items-center gap-2",children:[e.jsx("code",{className:"min-w-0 flex-1 truncate rounded-md bg-muted px-2 py-1 font-mono text-[12px]",children:o.hook.url}),e.jsx(z,{text:o.hook.url,label:"Copy the payload URL"})]}),e.jsx("dt",{className:"text-muted-foreground",children:"Content type"}),e.jsx("dd",{className:"m-0",children:e.jsx("code",{className:"font-mono text-[12px]",children:"application/json"})}),f&&e.jsxs(e.Fragment,{children:[e.jsx("dt",{className:"text-muted-foreground",children:"Secret"}),e.jsxs("dd",{className:"m-0 flex items-center gap-2",children:[e.jsx("code",{className:"min-w-0 flex-1 truncate rounded-md bg-muted px-2 py-1 font-mono text-[12px]","data-testid":"webhook-secret",children:f}),e.jsx(z,{text:f,label:"Copy the secret"})]})]}),e.jsx("dt",{className:"text-muted-foreground",children:"Events"}),e.jsx("dd",{className:"m-0",children:o.provider==="linear"?"Issues":o.provider==="jira"?"Issue: updated, deleted":"Let me select individual events → Issues"})]}),e.jsx(H,{children:e.jsx(y,{type:"button",onClick:i,children:"Done"})})]})})}function pe({open:o,onOpenChange:i,sites:x,onDone:f}){const[S,w]=a.useState(""),[l,u]=a.useState(""),[r,g]=a.useState(""),[j,m]=a.useState(""),[b,N]=a.useState(!1),[k,v]=a.useState(""),h=F({mutationFn:s=>T.connectTracker(s,"linear",{team:l.trim(),apiKey:r.trim(),...j.trim()?{webhookSecret:j.trim()}:{},createOn:b?"thread.created":"manual"})}),d=S||x[0]?.uid||"",t=async s=>{s.preventDefault(),v("");try{const c=await h.mutateAsync(d);u(""),g(""),m(""),N(!1),i(!1),f(d,c.webhook??{url:`${location.origin}/api/v1/integrations/linear/inbound`}),I.success("Linear connected")}catch(c){v(c instanceof K?c.message:"Could not reach the server.")}};return e.jsx(E,{open:o,onOpenChange:i,children:e.jsx(O,{children:e.jsxs("form",{onSubmit:s=>void t(s),className:"flex flex-col gap-4",noValidate:!0,children:[e.jsxs(W,{children:[e.jsx(R,{children:"Connect a site to Linear"}),e.jsx(J,{children:"A personal API key from Linear › Settings › API, belonging to a member of the team. It is checked against the team before it is saved, and stored encrypted with this instance's secret key."})]}),k&&e.jsx("p",{role:"alert",className:"m-0 rounded-md border border-destructive/40 bg-warn-soft px-3 py-2 text-[13px] text-destructive",children:k}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(C,{htmlFor:"ln-site",children:"Site"}),e.jsx("select",{id:"ln-site",value:d,onChange:s=>w(s.target.value),className:"h-8 rounded-md border border-input bg-background px-2.5 text-[13px]",children:x.map(s=>e.jsx("option",{value:s.uid,children:s.name},s.uid))})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(C,{htmlFor:"ln-team",children:"Team key"}),e.jsx(A,{id:"ln-team",placeholder:"ENG",value:l,onChange:s=>u(s.target.value),autoComplete:"off"}),e.jsx("p",{className:"m-0 text-[12px] text-muted-foreground",children:"The prefix of the team's issues, like the ENG in ENG-123."})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(C,{htmlFor:"ln-key",children:"API key"}),e.jsx(A,{id:"ln-key",type:"password",placeholder:"lin_api_…",value:r,onChange:s=>g(s.target.value),autoComplete:"off"})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(C,{htmlFor:"ln-secret",children:"Webhook signing secret"}),e.jsx(A,{id:"ln-secret",type:"password",placeholder:"from Linear's webhook, optional",value:j,onChange:s=>m(s.target.value),autoComplete:"off"}),e.jsx("p",{className:"m-0 text-[12px] text-muted-foreground",children:"Lets Linear move comments here when issues move there. The URL to give Linear is shown after connecting."})]}),e.jsxs("label",{className:"flex cursor-pointer items-start gap-2.5 text-[13px]",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:b,onChange:s=>N(s.target.checked)}),e.jsxs("span",{children:[e.jsx("span",{className:"block font-medium",children:"An issue for every new comment"}),e.jsx("span",{className:"block text-[12px] text-muted-foreground",children:"Off by default. Make issues from the brief when you mean it."})]})]}),e.jsxs(H,{children:[e.jsx(y,{type:"button",variant:"outline",onClick:()=>i(!1),children:"Cancel"}),e.jsx(y,{type:"submit",loading:h.isPending,disabled:!l.trim()||!r.trim()||!d,children:h.isPending?"Checking…":"Connect"})]})]})})})}function xe({open:o,onOpenChange:i,sites:x,onDone:f}){const[S,w]=a.useState(""),[l,u]=a.useState(""),[r,g]=a.useState(""),[j,m]=a.useState(""),[b,N]=a.useState(""),[k,v]=a.useState(""),[h,d]=a.useState(!1),[t,s]=a.useState(""),c=F({mutationFn:p=>T.connectTracker(p,"jira",{siteUrl:l.trim(),project:r.trim(),email:j.trim(),apiToken:b.trim(),...k.trim()?{webhookSecret:k.trim()}:{},createOn:h?"thread.created":"manual"})}),n=S||x[0]?.uid||"",P=async p=>{p.preventDefault(),s("");try{const L=await c.mutateAsync(n);g(""),N(""),v(""),d(!1),i(!1),f(n,L.webhook??{url:`${location.origin}/api/v1/integrations/jira/inbound`}),I.success("Jira connected")}catch(L){s(L instanceof K?L.message:"Could not reach the server.")}},$=(p,L,V,Q,Y,Z="text",G)=>e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(C,{htmlFor:`jr-${p}`,children:L}),e.jsx(A,{id:`jr-${p}`,type:Z,placeholder:Y,value:V,onChange:X=>Q(X.target.value),autoComplete:"off"}),G&&e.jsx("p",{className:"m-0 text-[12px] text-muted-foreground",children:G})]});return e.jsx(E,{open:o,onOpenChange:i,children:e.jsx(O,{children:e.jsxs("form",{onSubmit:p=>void P(p),className:"flex flex-col gap-4",noValidate:!0,children:[e.jsxs(W,{children:[e.jsx(R,{children:"Connect a site to Jira"}),e.jsx(J,{children:"An API token from id.atlassian.com › Security, with the email of the account it belongs to. It is checked against the project before it is saved, and stored encrypted with this instance's secret key."})]}),t&&e.jsx("p",{role:"alert",className:"m-0 rounded-md border border-destructive/40 bg-warn-soft px-3 py-2 text-[13px] text-destructive",children:t}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(C,{htmlFor:"jr-site",children:"Site"}),e.jsx("select",{id:"jr-site",value:n,onChange:p=>w(p.target.value),className:"h-8 rounded-md border border-input bg-background px-2.5 text-[13px]",children:x.map(p=>e.jsx("option",{value:p.uid,children:p.name},p.uid))})]}),$("url","Jira site",l,u,"https://kilnwork.atlassian.net"),$("project","Project key",r,g,"KAN","text","The prefix of the project's issues, like the KAN in KAN-12."),$("email","Account email",j,m,"you@kilnwork.studio","email"),$("token","API token",b,N,"","password"),$("secret","Webhook secret",k,v,"the secret you typed into Jira's webhook, optional","password","Lets Jira move comments here when issues move there. The URL to give Jira is shown after connecting."),e.jsxs("label",{className:"flex cursor-pointer items-start gap-2.5 text-[13px]",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:h,onChange:p=>d(p.target.checked)}),e.jsxs("span",{children:[e.jsx("span",{className:"block font-medium",children:"An issue for every new comment"}),e.jsx("span",{className:"block text-[12px] text-muted-foreground",children:"Off by default. Make issues from the brief when you mean it."})]})]}),e.jsxs(H,{children:[e.jsx(y,{type:"button",variant:"outline",onClick:()=>i(!1),children:"Cancel"}),e.jsx(y,{type:"submit",loading:c.isPending,disabled:!l.trim()||!r.trim()||!j.trim()||!b.trim()||!n,children:c.isPending?"Checking…":"Connect"})]})]})})})}function fe({open:o,onOpenChange:i,sites:x,onDone:f}){const[S,w]=a.useState(""),[l,u]=a.useState(""),[r,g]=a.useState(""),[j,m]=a.useState(!0),[b,N]=a.useState(""),k=B({queryKey:["integrations","slack","manifest"],queryFn:()=>T.slackManifest(),enabled:o,staleTime:1/0}),v=F({mutationFn:t=>T.connectTracker(t,"slack",{channel:l.trim(),token:r.trim(),createOn:j?"thread.created":"manual"})}),h=S||x[0]?.uid||"",d=async t=>{t.preventDefault(),N("");try{await v.mutateAsync(h),u(""),g(""),m(!0),i(!1),f(h),I.success("Slack connected")}catch(s){N(s instanceof K?s.message:"Could not reach the server.")}};return e.jsx(E,{open:o,onOpenChange:i,children:e.jsx(O,{children:e.jsxs("form",{onSubmit:t=>void d(t),className:"flex flex-col gap-4",noValidate:!0,children:[e.jsxs(W,{children:[e.jsx(R,{children:"Connect a site to Slack"}),e.jsx(J,{children:"Create the app in your workspace from our manifest — one click, the scopes are filled in — install it, and paste its Bot User OAuth Token. The token is checked against the channel before it is saved, and stored encrypted with this instance's secret key."})]}),b&&e.jsx("p",{role:"alert",className:"m-0 rounded-md border border-destructive/40 bg-warn-soft px-3 py-2 text-[13px] text-destructive",children:b}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(C,{htmlFor:"sl-site",children:"Site"}),e.jsx("select",{id:"sl-site",value:h,onChange:t=>w(t.target.value),className:"h-8 rounded-md border border-input bg-background px-2.5 text-[13px]",children:x.map(t=>e.jsx("option",{value:t.uid,children:t.name},t.uid))})]}),e.jsxs("div",{className:"flex items-center gap-3 rounded-md border border-border px-3 py-2 text-[13px]",children:[e.jsxs("span",{className:"flex-1",children:[e.jsx("span",{className:"block font-medium",children:"1. Create the app"}),e.jsx("span",{className:"block text-[12px] text-muted-foreground",children:"Opens api.slack.com with the manifest pasted; then Install to Workspace."})]}),e.jsx(y,{type:"button",variant:"outline",size:"sm",disabled:!k.data,render:e.jsx("a",{href:k.data?.url??"#",target:"_blank",rel:"noreferrer"}),children:"Create app…"})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(C,{htmlFor:"sl-token",children:"2. Bot User OAuth Token"}),e.jsx(A,{id:"sl-token",type:"password",placeholder:"xoxb-…",value:r,onChange:t=>g(t.target.value),autoComplete:"off"}),e.jsx("p",{className:"m-0 text-[12px] text-muted-foreground",children:"On the app's OAuth & Permissions page after installing."})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(C,{htmlFor:"sl-channel",children:"3. Channel"}),e.jsx(A,{id:"sl-channel",placeholder:"#reviews",value:l,onChange:t=>u(t.target.value),autoComplete:"off"}),e.jsx("p",{className:"m-0 text-[12px] text-muted-foreground",children:"A public channel by name; a private one needs the app invited first, then its name works too."})]}),e.jsxs("label",{className:"flex cursor-pointer items-start gap-2.5 text-[13px]",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:j,onChange:t=>m(t.target.checked)}),e.jsxs("span",{children:[e.jsx("span",{className:"block font-medium",children:"A message for every new comment"}),e.jsx("span",{className:"block text-[12px] text-muted-foreground",children:"On by default: a channel is where people are told. Turn it off to post from the brief when you mean it."})]})]}),e.jsxs(H,{children:[e.jsx(y,{type:"button",variant:"outline",onClick:()=>i(!1),children:"Cancel"}),e.jsx(y,{type:"submit",loading:v.isPending,disabled:!l.trim()||!r.trim()||!h,children:v.isPending?"Checking…":"Connect"})]})]})})})}const Ee=()=>U(q().user.role,"settings")?e.jsx(he,{}):e.jsx(ae,{title:"Integrations",children:e.jsxs("p",{className:"m-0",children:["Connecting a site to GitHub, Linear, Jira or Slack is done by an owner or an admin. Your own agent tokens are on"," ",e.jsx(ee,{to:"/you",className:"text-brand hover:underline",children:"You"}),"."]})});export{Ee as component};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import{c as Q,j as e,h as U,q as L,a2 as W,r as j,B as h,a3 as v,a4 as M,Z as k,D as X,H as Z,a5 as ee,a6 as se,a7 as te,I as ie,z as C}from"./index-COORBw3J.js";import{u as S}from"./useQuery-DT3w11tR.js";import{u as ae,a as ne}from"./zod-D7wsVto4.js";import{A as le,a as re}from"./avatar-nrKPMD7A.js";import{B as oe}from"./badge-DEvpNOzs.js";import{C as B}from"./card--8RLEIGP.js";import{X as de,E as me,G as ce,I as R,H as A,J as I,K as D}from"./dialog-D0b9dX2m.js";import{L as E,I as xe}from"./label-Ds2CYKth.js";import{T as P,a as T,b as f,c,d as $,e as x}from"./table-CgakQRAC.js";import{C as he}from"./ConfirmDialog-D6hyhISD.js";import{C as ue}from"./CopyButton-hyiQh1Zr.js";import{A as pe,P as je,b as fe}from"./parts-B46j7QJi.js";import{u as O,a as ge}from"./labels-CUVq_Uf0.js";import{c as ve,d as be,e as ye,f as Ne,g as we}from"./studio-CSGo60dt.js";import{P as ke}from"./plus-yZPLf0r6.js";import"./useBaseQuery-DG87f6yY.js";import"./useRender-Bu6Vyu1g.js";import"./check-XHwuM5zg.js";import"./copy-eakuUTpI.js";/**
|
|
2
|
-
* @license lucide-react v1.39.0 - ISC
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the ISC license.
|
|
5
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
6
|
-
*/const Ce=[["path",{d:"m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7",key:"132q7q"}],["rect",{x:"2",y:"4",width:"20",height:"16",rx:"2",key:"izxlao"}]],Se=Q("mail",Ce);function z({className:s,...n}){return e.jsx("select",{className:U("h-8 rounded-md border border-input bg-card px-2.5 text-[13.5px] text-foreground outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:opacity-50",s),...n})}function Re(){const s=O(),n=L(s.user.role,"people"),l=W(s.user.role),m=S(C.members()),t=S(C.invites()),[d,r]=j.useState(!1),o=s.limits.members.limit,i=o!==null&&s.limits.members.used>=o;return e.jsxs("div",{className:"p-7",children:[e.jsx(je,{title:"Members",sub:`${n?"Only owners change roles or remove people.":"Admins invite members. Ask an owner to change a role or remove someone."}${o!==null?` ${s.limits.members.used} of ${o} members.`:""}`,actions:l.length?e.jsxs(h,{onClick:()=>r(!0),disabled:i,title:i?`This studio's plan allows ${o} members.`:void 0,children:[e.jsx(ke,{}),"Invite"]}):void 0}),e.jsx(B,{className:"max-w-[1040px] overflow-hidden py-0",children:e.jsxs(P,{children:[e.jsx(T,{children:e.jsxs(f,{children:[e.jsx(c,{children:"Name"}),e.jsx(c,{children:"Email"}),e.jsx(c,{children:"Role"}),n&&e.jsx(c,{className:"w-0"})]})}),e.jsx($,{children:(m.data?.data??[]).map(u=>e.jsx(De,{m:u,owner:n},u.email))})]})}),e.jsx(Ae,{}),(t.data?.data.length??0)>0&&e.jsx(Ie,{invites:t.data.data,roles:l}),e.jsxs("p",{className:"m-0 mt-3 max-w-[1040px] text-[12.5px] text-muted-foreground",children:["People join through an invite link and choose their own password. A forgotten one is reset on the server with ",e.jsx("code",{className:"font-mono text-[12px]",children:"snagset passwd"}),"."]}),l.length>0&&e.jsx(Le,{open:d,onOpenChange:r,mail:t.data?.mail??!1,roles:l})]})}const _=s=>new Date(s).toLocaleDateString(void 0,{day:"numeric",month:"short"}),b={owner:"Owner",admin:"Admin",member:"Member"};function Ae(){return e.jsx("dl",{className:"m-0 mt-3 grid max-w-[1040px] grid-cols-[70px_1fr] gap-x-3 gap-y-1 text-[12.5px]","aria-label":"Roles",children:v.map(s=>e.jsxs("div",{className:"contents",children:[e.jsx("dt",{className:"font-medium",children:b[s]}),e.jsx("dd",{className:"m-0 text-muted-foreground",children:M[s]})]},s))})}function Ie({invites:s,roles:n}){const l=ye(),m=n.length>0;return e.jsxs("section",{className:"mt-6 max-w-[1040px]","aria-labelledby":"pending-invites",children:[e.jsx("div",{id:"pending-invites",className:"mb-2",children:e.jsx(fe,{children:"Pending invites"})}),e.jsx(B,{className:"overflow-hidden py-0",children:e.jsxs(P,{children:[e.jsx(T,{children:e.jsxs(f,{children:[e.jsx(c,{children:"For"}),e.jsx(c,{children:"Role"}),e.jsx(c,{children:"Invited by"}),e.jsx(c,{children:"Expires"}),m&&e.jsx(c,{className:"w-0"})]})}),e.jsx($,{children:s.map(t=>e.jsxs(f,{children:[e.jsx(x,{className:t.email?"":"text-muted-foreground",children:t.email??"Anyone with the link"}),e.jsx(x,{className:"capitalize",children:t.role}),e.jsx(x,{className:"text-muted-foreground",children:t.createdBy?t.createdBy.name||t.createdBy.email:"—"}),e.jsx(x,{className:"text-muted-foreground",children:_(t.expiresAt)}),m&&e.jsx(x,{className:"text-right",children:n.includes(t.role)&&e.jsx(h,{variant:"ghost",size:"sm","aria-label":`Revoke the invite for ${t.email??"anyone with the link"}`,disabled:l.isPending&&l.variables===t.id,onClick:()=>l.mutate(t.id),children:"Revoke"})})]},t.id))})]})})]})}function De({m:s,owner:n}){const l=ve(),m=be(),[t,d]=j.useState(!1);return e.jsxs(f,{children:[e.jsx(x,{children:e.jsxs("span",{className:"flex items-center gap-2.5",children:[e.jsx(le,{className:"size-6",children:e.jsx(re,{className:"bg-brand-soft text-[10px] font-semibold text-brand",children:ge(s.name,s.email)})}),e.jsx("span",{className:"font-medium",children:s.name||s.email}),s.you&&e.jsx(oe,{variant:"outline",className:"text-[11px]",children:"you"})]})}),e.jsx(x,{className:"text-muted-foreground",children:s.email}),e.jsx(x,{children:n?e.jsx(z,{"aria-label":`Role for ${s.email}`,value:s.role,onChange:r=>l.mutate({email:s.email,role:r.target.value}),children:v.map(r=>e.jsx("option",{value:r,children:b[r]},r))}):e.jsx("span",{className:"capitalize",children:s.role})}),n&&e.jsxs(x,{className:"text-right",children:[!s.you&&e.jsx(h,{variant:"ghost",size:"icon-sm","aria-label":`Remove ${s.email}`,onClick:()=>d(!0),children:e.jsx(de,{})}),e.jsx(he,{open:t,onOpenChange:d,title:`Remove ${s.name||s.email}?`,description:"Every session of theirs ends now. Nothing they wrote is destroyed.",confirmLabel:"Remove",busy:m.isPending,onConfirm:()=>{d(!1),m.mutate(s.email)}})]})]})}const Ee=Z({email:se([te(""),ie().trim().email("An email address, or leave it empty")]),role:ee(v)});function Le({open:s,onOpenChange:n,mail:l,roles:m}){const t=Ne(),d=we(),[r,o]=j.useState(""),[i,u]=j.useState(null),[y,N]=j.useState(null),{register:w,handleSubmit:F,formState:p,reset:H,watch:q}=ae({resolver:ne(Ee),defaultValues:{role:"member",email:""}}),G=q("role"),g=a=>{n(a),a||(u(null),N(null),o(""),d.reset(),H())},V=F(async a=>{o("");try{u(await t.mutateAsync({role:a.role,...a.email?{email:a.email}:{}}))}catch(K){o(k(K))}}),Y=async()=>{if(i){o("");try{N((await d.mutateAsync({id:i.invite.id,token:i.token})).to)}catch(a){o(k(a))}}},J=(i?.mail??l)&&!!i?.invite.email;return e.jsx(me,{open:s,onOpenChange:g,children:e.jsx(ce,{children:i?e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsxs(R,{children:[e.jsx(A,{children:"Invite link"}),e.jsxs(I,{children:[i.invite.email?`${i.invite.email} can`:"Anyone with this link can"," join as ",X(i.invite.role)," ","until ",_(i.invite.expiresAt),". It works once, and this is the only time it is shown."]})]}),r&&e.jsx("p",{role:"alert",className:"m-0 rounded-md border border-destructive/40 bg-warn-soft px-3 py-2 text-[13px] text-destructive",children:r}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("code",{className:"min-w-0 flex-1 truncate rounded-md bg-muted px-2 py-1.5 font-mono text-[12px]","data-testid":"invite-url",children:i.url}),e.jsx(ue,{text:i.url,label:"Copy link",withText:!0})]}),J&&(y?e.jsxs("p",{role:"status",className:"m-0 text-[13px] text-muted-foreground",children:["Sent to ",y,"."]}):e.jsxs(h,{type:"button",variant:"outline",className:"self-start",loading:d.isPending,onClick:()=>void Y(),children:[e.jsx(Se,{}),d.isPending?"Sending…":`Send by email to ${i.invite.email}`]})),e.jsx(D,{children:e.jsx(h,{type:"button",onClick:()=>g(!1),children:"Done"})})]}):e.jsxs("form",{onSubmit:V,className:"flex flex-col gap-4",noValidate:!0,children:[e.jsxs(R,{children:[e.jsx(A,{children:"Invite someone"}),e.jsx(I,{children:"You get a link to send them. Whoever opens it chooses their own name and password, and is signed in. It works once and lasts seven days."})]}),r&&e.jsx("p",{role:"alert",className:"m-0 rounded-md border border-destructive/40 bg-warn-soft px-3 py-2 text-[13px] text-destructive",children:r}),e.jsxs("fieldset",{disabled:p.isSubmitting,className:"m-0 flex min-w-0 flex-col gap-4 border-0 p-0",children:[e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(E,{htmlFor:"invite-role",children:"Role"}),e.jsx(z,{id:"invite-role",className:"w-full",...w("role"),children:["member","admin","owner"].filter(a=>m.includes(a)).map(a=>e.jsx("option",{value:a,children:b[a]},a))}),e.jsx("p",{className:"m-0 text-[12px] text-muted-foreground","data-testid":"invite-role-summary",children:M[G]??""})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsxs(E,{htmlFor:"invite-email",children:["Email ",e.jsx("span",{className:"font-normal text-muted-foreground",children:"optional"})]}),e.jsx(xe,{id:"invite-email",type:"email",autoComplete:"off","aria-invalid":!!p.errors.email,...w("email")}),e.jsx("p",{className:"m-0 text-[12px] text-muted-foreground",children:p.errors.email?.message??(l?"Given, only that address can use the link, and you can mail it to them.":"Given, only that address can use the link.")})]}),e.jsxs(D,{children:[e.jsx(h,{type:"button",variant:"outline",onClick:()=>g(!1),children:"Cancel"}),e.jsx(h,{type:"submit",loading:p.isSubmitting,children:p.isSubmitting?"Creating…":"Create invite"})]})]})]})})})}const Xe=()=>L(O().user.role,"settings")?e.jsx(Re,{}):e.jsx(pe,{title:"Members"});export{Xe as component};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{u as r,Q as u}from"./useBaseQuery-DG87f6yY.js";function a(e,s){return r(e,u)}export{a as u};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{k as n}from"./index-COORBw3J.js";function u(e){return n(e.defaultTagName??"div",e,e)}export{u};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r,w as b,j as e,h as y,x as N,B as j,q as f,Z as S}from"./index-COORBw3J.js";import{L as c,I as d}from"./label-Ds2CYKth.js";import{P as C,a as h}from"./parts-B46j7QJi.js";import{S as P}from"./SaveState-CmcJ4unZ.js";import{M as A}from"./McpTokens--C1TeZqq.js";import{u as k}from"./labels-CUVq_Uf0.js";import{u as M,a as F}from"./studio-CSGo60dt.js";import{M as E,S as T,a as D}from"./sun-Djjj3CUF.js";import"./card--8RLEIGP.js";import"./useQuery-DT3w11tR.js";import"./useBaseQuery-DG87f6yY.js";import"./badge-DEvpNOzs.js";import"./useRender-Bu6Vyu1g.js";import"./dialog-D0b9dX2m.js";import"./switch-WMRF6OK1.js";import"./useAriaLabelledBy-obWCIEg1.js";import"./useValueChanged-CHzVruZ0.js";import"./ConfirmDialog-D6hyhISD.js";import"./CopyButton-hyiQh1Zr.js";import"./check-XHwuM5zg.js";import"./copy-eakuUTpI.js";function z({items:t}){const[a,n]=r.useState(b);return e.jsx("div",{className:"flex gap-2",role:"radiogroup","aria-label":"Theme",children:t.map(s=>e.jsxs("button",{type:"button",role:"radio","aria-checked":a===s.value,onClick:()=>{N(s.value),n(s.value)},className:y("inline-flex h-7 items-center gap-1.5 rounded-md border border-border bg-card px-2.5 text-[12.5px] font-medium text-ink-soft",a===s.value&&"border-line-strong bg-muted text-foreground"),children:[s.icon,s.label]},s.value))})}function ae(){const t=k(),a=M(),[n,s]=r.useState(t.user.name),[u,i]=r.useState(null);r.useEffect(()=>s(t.user.name),[t.user.name]);const m=n.trim()!==t.user.name;return e.jsxs("div",{className:"flex flex-col gap-10 p-7",children:[e.jsx(C,{title:"You",sub:"Your own account on this studio",actions:e.jsx(P,{saving:a.isPending,savedAt:u})}),e.jsxs(h,{title:"Profile",description:"How you appear on replies and in the members list.",children:[e.jsxs("form",{className:"flex flex-col gap-1.5",onSubmit:l=>{l.preventDefault(),m&&a.mutate(n.trim(),{onSuccess:()=>i(Date.now())})},children:[e.jsx(c,{htmlFor:"your-name",children:"Name"}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx(d,{id:"your-name",value:n,onChange:l=>s(l.target.value)}),m&&e.jsx(j,{type:"submit",variant:"outline",children:"Save"})]})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(c,{htmlFor:"your-email",children:"Email"}),e.jsx(d,{id:"your-email",value:t.user.email,readOnly:!0,className:"bg-muted text-ink-soft"}),e.jsxs("p",{className:"m-0 text-[12px] text-muted-foreground",children:["How you sign in, and how the server finds you. An owner changes it with"," ",e.jsx("code",{className:"font-mono",children:"snagset passwd"}),"."]})]})]}),e.jsx(h,{title:"Password",description:"Changing it keeps this session and ends every other one.",children:e.jsx(H,{})}),!f(t.user.role,"settings")&&f(t.user.role,"review")&&e.jsx(A,{owner:!0,mine:!0,instanceOwner:!1,sites:t.projects}),e.jsx(h,{title:"Appearance",description:"Also in your menu at the bottom of the sidebar.",children:e.jsx(z,{items:[{value:"system",label:"System",icon:e.jsx(E,{className:"size-3.5"})},{value:"light",label:"Light",icon:e.jsx(T,{className:"size-3.5"})},{value:"dark",label:"Dark",icon:e.jsx(D,{className:"size-3.5"})}]})})]})}function H(){const t=F(),[a,n]=r.useState(""),[s,u]=r.useState(""),[i,m]=r.useState(""),[l,g]=r.useState(""),[w,p]=r.useState(!1),x=a.length>0&&s.length>=12&&s===i;return e.jsxs("form",{className:"flex flex-col gap-4",onSubmit:async o=>{if(o.preventDefault(),!(!x||t.isPending)){g("");try{await t.mutateAsync({current:a,next:s}),n(""),u(""),m(""),p(!0)}catch(v){g(S(v))}}},children:[l&&e.jsx("p",{role:"alert",className:"m-0 rounded-md border border-destructive/40 bg-warn-soft px-3 py-2 text-[13px] text-destructive",children:l}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(c,{htmlFor:"pw-current",children:"Current password"}),e.jsx(d,{id:"pw-current",type:"password",autoComplete:"current-password",value:a,onChange:o=>{n(o.target.value),p(!1)}})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(c,{htmlFor:"pw-next",children:"New password"}),e.jsx(d,{id:"pw-next",type:"password",autoComplete:"new-password",value:s,onChange:o=>{u(o.target.value),p(!1)}}),e.jsx("p",{className:"m-0 text-[12px] text-muted-foreground",children:"At least 12 characters. No composition rules — length is what matters."})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx(c,{htmlFor:"pw-again",children:"Confirm new password"}),e.jsx(d,{id:"pw-again",type:"password",autoComplete:"new-password",value:i,onChange:o=>{m(o.target.value),p(!1)}}),i.length>0&&s!==i&&e.jsx("p",{className:"m-0 text-[12px] text-muted-foreground",children:"Those do not match yet."})]}),e.jsx("div",{className:"flex justify-end",children:e.jsx(j,{type:"submit",loading:t.isPending,disabled:!x,children:t.isPending?"Changing…":w&&!x?"Changed":"Change password"})})]})}export{ae as component};
|