UNPKG

@accounter/client

Version:
1 lines 15.7 kB
import{s as e}from"./dist-BmACi7k_.js";import{i as t,o as n}from"./utils-BxkqV8PM.js";import{Ai as r,Fi as i,Ii as a,Nn as o,Ro as s,ei as c,ni as l,r as u,t as d,ti as ee,wr as te,zo as f}from"./error-handling-sQ-z99nE.js";import{c as ne,t as re,y as ie}from"./routes-C4NpBRv7.js";import{a as ae,c as oe,d as p,dt as m,f as h,gt as g,i as _,l as v,m as y,n as b,p as x,r as S,s as C,t as se,u as w}from"./select-KYQbDWZT.js";import{H as T,Ir as ce,a as E,dr as D,i as le,ir as ue,o as de,s as O}from"./common-DtK-TFhT.js";import{y as k}from"./separator-CNv9f_S5.js";import{t as fe}from"./key-round-COt5zQFn.js";import{t as A}from"./button-BZ6z_NAl.js";import{$c as j,$n as pe,Bn as M,Gc as N,Jc as P,Pt as F,Uc as I,Wc as L,Xc as R,Yc as z,a as B,h as V,i as H,n as U,nl as me,o as W,qc as G,r as K,rn as he,t as q}from"./table-D4eAFW7G.js";import{n as J,r as Y,t as X}from"./alert-inbp4t8N.js";var ge=g(`shield-alert`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`M12 8v4`,key:`1got3b`}],[`path`,{d:`M12 16h.01`,key:`1drbdi`}]]),_e=g(`user-plus`,[[`path`,{d:`M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2`,key:`1yyitq`}],[`circle`,{cx:`9`,cy:`7`,r:`4`,key:`nufk8`}],[`line`,{x1:`19`,x2:`19`,y1:`8`,y2:`14`,key:`1bvyxn`}],[`line`,{x1:`22`,x2:`16`,y1:`11`,y2:`11`,key:`1shjgl`}]]),Z=e(n(),1),ve=`generateApiKey`,ye=()=>{let[{fetching:e,error:t},n]=s(te);return{fetching:e,error:t,generateApiKey:(0,Z.useCallback)(async({name:e,roleId:t})=>{let r=`Error generating API key`,i=ve;u.loading(`Generating API key`,{id:i});try{let a=d(await n({name:e,roleId:t}),r,i);if(a)return u.success(`Success`,{id:i,description:`API key generated successfully`}),a.generateApiKey}catch(e){console.error(r,e),u.error(`Error`,{id:i,description:r,duration:1e4,closeButton:!0})}},[n])}},be=`revokeApiKey`,xe=()=>{let[{fetching:e,error:t},n]=s(i);return{fetching:e,error:t,revokeApiKey:(0,Z.useCallback)(async e=>{let t=`Error revoking API key`,r=be;u.loading(`Revoking API key`,{id:r});try{let i=d(await n({id:e}),t,r);if(i)return i.revokeApiKey?(u.success(`Success`,{id:r,description:`API key revoked successfully`}),!0):(u.error(`Error`,{id:r,description:`Failed to revoke API key. It may have already been revoked.`}),!1)}catch(e){console.error(t,e),u.error(`Error`,{id:r,description:t,duration:1e4,closeButton:!0})}},[n])}},Q=t(),Se=[{value:`scraper`,label:`Scraper`},{value:`gmail_listener`,label:`Gmail Listener`}],Ce=M({name:pe().trim().min(1,`Name is required`),roleId:F([`scraper`,`gmail_listener`])});function we(){let[{data:e,fetching:t,error:n},r]=f({query:c}),{revokeApiKey:i,fetching:a}=xe(),o=(0,Z.useCallback)(()=>r({requestPolicy:`network-only`}),[r]),s=(0,Z.useCallback)(async e=>{await i(e)!==void 0&&o()},[i,o]),l=e?.listApiKeys??[];return(0,Q.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center justify-between gap-4`,children:[(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`h2`,{className:`text-lg font-medium`,children:`API Keys`}),(0,Q.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`Programmatic access keys for automated roles.`})]}),(0,Q.jsx)(Te,{onCreated:o})]}),n?(0,Q.jsxs)(X,{variant:`destructive`,children:[(0,Q.jsx)(Y,{children:`Failed to load API keys`}),(0,Q.jsx)(J,{children:n.message})]}):t?(0,Q.jsx)(`div`,{className:`flex justify-center p-8`,children:(0,Q.jsx)(m,{className:`h-8 w-8 animate-spin`})}):l.length===0?(0,Q.jsx)(`p`,{className:`p-6 text-center text-sm text-muted-foreground`,children:`No API keys yet.`}):(0,Q.jsxs)(q,{children:[(0,Q.jsx)(B,{children:(0,Q.jsxs)(W,{children:[(0,Q.jsx)(H,{children:`Name`}),(0,Q.jsx)(H,{children:`Role`}),(0,Q.jsx)(H,{children:`Last Used`}),(0,Q.jsx)(H,{children:`Created`}),(0,Q.jsx)(H,{className:`text-right`,children:`Actions`})]})}),(0,Q.jsx)(U,{children:l.map(e=>(0,Q.jsxs)(W,{children:[(0,Q.jsx)(K,{className:`font-medium`,children:e.name}),(0,Q.jsx)(K,{children:Se.find(t=>t.value===e.roleId)?.label??e.roleId}),(0,Q.jsx)(K,{children:e.lastUsedAt?new Date(e.lastUsedAt).toLocaleString():`Never`}),(0,Q.jsx)(K,{children:new Date(e.createdAt).toLocaleDateString()}),(0,Q.jsx)(K,{className:`text-right`,children:(0,Q.jsx)(T,{onConfirm:()=>s(e.id),title:`Revoke "${e.name}"? This cannot be undone and any client using it will lose access.`,children:(0,Q.jsxs)(A,{variant:`destructive`,size:`sm`,disabled:a,children:[(0,Q.jsx)(k,{className:`size-4`}),`Revoke`]})})})]},e.id))})]})]})}function Te({onCreated:e}){let[t,n]=(0,Z.useState)(!1),[r,i]=(0,Z.useState)(null),{generateApiKey:a,fetching:o}=ye(),s=j({resolver:V(Ce),defaultValues:{name:``,roleId:`scraper`}}),c=(0,Z.useCallback)(async t=>{let n=await a(t);n?.apiKey&&(i(n.apiKey),e())},[a,e]),l=(0,Z.useCallback)(e=>{n(e),e||(i(null),s.reset())},[s]),d=(0,Z.useCallback)(()=>{r&&(ue(r),u.success(`Copied`,{description:`API key copied to clipboard`}))},[r]);return(0,Q.jsxs)(oe,{open:t,onOpenChange:l,children:[(0,Q.jsx)(y,{asChild:!0,children:(0,Q.jsxs)(A,{children:[(0,Q.jsx)(fe,{className:`size-4`}),`Generate New Key`]})}),(0,Q.jsx)(v,{onPointerDownOutside:e=>{r&&e.preventDefault()},onEscapeKeyDown:e=>{r&&e.preventDefault()},children:r?(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsxs)(h,{children:[(0,Q.jsx)(x,{children:`API Key Created`}),(0,Q.jsx)(w,{children:`Copy your key now — it will not be shown again.`})]}),(0,Q.jsxs)(X,{variant:`destructive`,children:[(0,Q.jsx)(ge,{className:`size-4`}),(0,Q.jsx)(Y,{children:`Save this key immediately`}),(0,Q.jsx)(J,{children:`For security, the plaintext key is shown only once and cannot be retrieved later.`})]}),(0,Q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,Q.jsx)(C,{readOnly:!0,value:r,className:`font-mono text-xs`}),(0,Q.jsxs)(A,{type:`button`,variant:`outline`,size:`icon`,onClick:d,children:[(0,Q.jsx)(ce,{className:`size-4`}),(0,Q.jsx)(`span`,{className:`sr-only`,children:`Copy API key`})]})]}),(0,Q.jsx)(p,{children:(0,Q.jsx)(A,{type:`button`,onClick:()=>l(!1),children:`Done`})})]}):(0,Q.jsxs)(Q.Fragment,{children:[(0,Q.jsxs)(h,{children:[(0,Q.jsx)(x,{children:`Generate New API Key`}),(0,Q.jsx)(w,{children:`Create a key for programmatic access. Choose a descriptive name and a role.`})]}),(0,Q.jsx)(L,{...s,children:(0,Q.jsxs)(`form`,{onSubmit:s.handleSubmit(c),className:`flex flex-col gap-4`,children:[(0,Q.jsx)(G,{control:s.control,name:`name`,render:({field:e})=>(0,Q.jsxs)(P,{children:[(0,Q.jsx)(z,{children:`Name`}),(0,Q.jsx)(N,{children:(0,Q.jsx)(C,{placeholder:`e.g. Production Scraper`,...e})}),(0,Q.jsx)(R,{})]})}),(0,Q.jsx)(G,{control:s.control,name:`roleId`,render:({field:e})=>(0,Q.jsxs)(P,{children:[(0,Q.jsx)(z,{children:`Role`}),(0,Q.jsxs)(se,{onValueChange:e.onChange,value:e.value,children:[(0,Q.jsx)(N,{children:(0,Q.jsx)(_,{children:(0,Q.jsx)(ae,{placeholder:`Select a role`})})}),(0,Q.jsx)(b,{children:Se.map(e=>(0,Q.jsx)(S,{value:e.value,children:e.label},e.value))})]}),(0,Q.jsx)(R,{})]})}),(0,Q.jsx)(p,{children:(0,Q.jsxs)(A,{type:`submit`,disabled:o,children:[o&&(0,Q.jsx)(m,{className:`size-4 animate-spin`}),`Generate`]})})]})})]})})]})}var Ee=`removeBusinessUser`,De=()=>{let[{fetching:e,error:t},n]=s(r);return{fetching:e,error:t,removeBusinessUser:(0,Z.useCallback)(async e=>{let t=`Error removing user`,r=Ee;u.loading(`Removing user`,{id:r});try{let i=d(await n({userId:e}),t,r);if(i)return i.removeBusinessUser?(u.success(`Success`,{id:r,description:`User removed successfully`}),!0):(u.error(`Error`,{id:r,description:`Failed to remove user. They may have already been removed.`}),!1)}catch(e){console.error(t,e),u.error(`Error`,{id:r,description:t,duration:1e4,closeButton:!0})}},[n])}},Oe={business_owner:`Business Owner`,accountant:`Accountant`,employee:`Employee`,scraper:`Scraper`,gmail_listener:`Gmail Listener`},ke=e=>Oe[e]??e;function Ae(){let[{data:e,fetching:t,error:n},r]=f({query:ee}),{removeBusinessUser:i,fetching:a}=De(),o=(0,Z.useCallback)(()=>r({requestPolicy:`network-only`}),[r]),s=(0,Z.useCallback)(async e=>{await i(e)!==void 0&&o()},[i,o]),c=e?.listBusinessUsers??[];return(0,Q.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`h2`,{className:`text-lg font-medium`,children:`Users`}),(0,Q.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`People with access to this business.`})]}),n?(0,Q.jsxs)(X,{variant:`destructive`,children:[(0,Q.jsx)(Y,{children:`Failed to load users`}),(0,Q.jsx)(J,{children:n.message})]}):t?(0,Q.jsx)(je,{}):c.length===0?(0,Q.jsx)(`p`,{className:`p-6 text-center text-sm text-muted-foreground`,children:`No users found.`}):(0,Q.jsxs)(q,{children:[(0,Q.jsx)(B,{children:(0,Q.jsxs)(W,{children:[(0,Q.jsx)(H,{children:`Name`}),(0,Q.jsx)(H,{children:`Email`}),(0,Q.jsx)(H,{children:`Role`}),(0,Q.jsx)(H,{className:`text-right`,children:`Actions`})]})}),(0,Q.jsx)(U,{children:c.map(e=>(0,Q.jsxs)(W,{children:[(0,Q.jsx)(K,{className:`font-medium`,children:e.name?.trim()||(0,Q.jsx)(`span`,{className:`text-muted-foreground`,children:`—`})}),(0,Q.jsx)(K,{children:e.email}),(0,Q.jsx)(K,{children:(0,Q.jsx)(I,{variant:`secondary`,children:ke(e.roleId)})}),(0,Q.jsx)(K,{className:`text-right`,children:(0,Q.jsx)(T,{onConfirm:()=>s(e.id),title:`Remove ${e.name?.trim()?`${e.name.trim()} (${e.email})`:e.email} from this business? They will lose access immediately.`,children:(0,Q.jsxs)(A,{variant:`destructive`,size:`sm`,disabled:a,children:[(0,Q.jsx)(k,{className:`size-4`}),`Remove User`]})})})]},e.id))})]})]})}function je(){return(0,Q.jsxs)(q,{children:[(0,Q.jsx)(B,{children:(0,Q.jsxs)(W,{children:[(0,Q.jsx)(H,{children:`Name`}),(0,Q.jsx)(H,{children:`Email`}),(0,Q.jsx)(H,{children:`Role`}),(0,Q.jsx)(H,{className:`text-right`,children:`Actions`})]})}),(0,Q.jsx)(U,{children:Array.from({length:4}).map((e,t)=>(0,Q.jsxs)(W,{children:[(0,Q.jsx)(K,{children:(0,Q.jsx)(D,{className:`h-4 w-32`})}),(0,Q.jsx)(K,{children:(0,Q.jsx)(D,{className:`h-4 w-48`})}),(0,Q.jsx)(K,{children:(0,Q.jsx)(D,{className:`h-6 w-24`})}),(0,Q.jsx)(K,{className:`text-right`,children:(0,Q.jsx)(D,{className:`ml-auto h-8 w-28`})})]},t))})]})}var Me=`createInvitation`,Ne=()=>{let[{fetching:e,error:t},n]=s(o);return{fetching:e,error:t,createInvitation:(0,Z.useCallback)(async({email:e,roleId:t})=>{let r=`Error creating invitation`,i=Me;u.loading(`Creating invitation`,{id:i});try{let a=d(await n({email:e,roleId:t}),r,i);if(a)return u.success(`Success`,{id:i,description:`Invitation sent successfully`}),a.createInvitation}catch(e){console.error(r,e),u.error(`Error`,{id:i,description:e instanceof Error?e.message:r,duration:1e4,closeButton:!0})}},[n])}},Pe=`revokeInvitation`,Fe=()=>{let[{fetching:e,error:t},n]=s(a);return{fetching:e,error:t,revokeInvitation:(0,Z.useCallback)(async e=>{let t=`Error revoking invitation`,r=Pe;u.loading(`Revoking invitation`,{id:r});try{let i=d(await n({id:e}),t,r);if(i)return i.revokeInvitation?(u.success(`Success`,{id:r,description:`Invitation revoked successfully`}),!0):(u.error(`Error`,{id:r,description:`Failed to revoke invitation. It may have already been revoked.`}),!1)}catch(e){console.error(t,e),u.error(`Error`,{id:r,description:t,duration:1e4,closeButton:!0})}},[n])}},$={business_owner:`Business Owner`,accountant:`Accountant`,employee:`Employee`,scraper:`Scraper`,gmail_listener:`Gmail Listener`},Ie=[{value:`business_owner`,label:$.business_owner},{value:`accountant`,label:$.accountant},{value:`employee`,label:$.employee}],Le=e=>$[e]??e,Re=M({email:he(`Please enter a valid email address`),roleId:F([`business_owner`,`accountant`,`employee`])});function ze(){let[{data:e,fetching:t,error:n},r]=f({query:l}),{revokeInvitation:i,fetching:a}=Fe(),o=(0,Z.useCallback)(()=>r({requestPolicy:`network-only`}),[r]),s=(0,Z.useCallback)(async e=>{await i(e)!==void 0&&o()},[i,o]),c=e?.listInvitations??[];return(0,Q.jsxs)(`div`,{className:`flex flex-col gap-4`,children:[(0,Q.jsxs)(`div`,{className:`flex items-center justify-between gap-4`,children:[(0,Q.jsxs)(`div`,{children:[(0,Q.jsx)(`h2`,{className:`text-lg font-medium`,children:`Invitations`}),(0,Q.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`Pending invitations to join this business.`})]}),(0,Q.jsx)(Be,{onInvited:o})]}),n?(0,Q.jsxs)(X,{variant:`destructive`,children:[(0,Q.jsx)(Y,{children:`Failed to load invitations`}),(0,Q.jsx)(J,{children:n.message})]}):t?(0,Q.jsx)(`div`,{className:`flex justify-center p-8`,children:(0,Q.jsx)(m,{className:`h-8 w-8 animate-spin`})}):c.length===0?(0,Q.jsx)(`p`,{className:`p-6 text-center text-sm text-muted-foreground`,children:`No pending invitations.`}):(0,Q.jsxs)(q,{children:[(0,Q.jsx)(B,{children:(0,Q.jsxs)(W,{children:[(0,Q.jsx)(H,{children:`Email`}),(0,Q.jsx)(H,{children:`Role`}),(0,Q.jsx)(H,{children:`Expires`}),(0,Q.jsx)(H,{className:`text-right`,children:`Actions`})]})}),(0,Q.jsx)(U,{children:c.map(e=>(0,Q.jsxs)(W,{children:[(0,Q.jsx)(K,{className:`font-medium`,children:e.email}),(0,Q.jsx)(K,{children:(0,Q.jsx)(I,{variant:`secondary`,children:Le(e.roleId)})}),(0,Q.jsx)(K,{children:new Date(e.expiresAt).toLocaleDateString()}),(0,Q.jsx)(K,{className:`text-right`,children:(0,Q.jsx)(T,{onConfirm:()=>s(e.id),title:`Revoke the invitation for "${e.email}"? They will no longer be able to accept it.`,children:(0,Q.jsxs)(A,{variant:`destructive`,size:`sm`,disabled:a,children:[(0,Q.jsx)(k,{className:`size-4`}),`Revoke`]})})})]},e.id))})]})]})}function Be({onInvited:e}){let[t,n]=(0,Z.useState)(!1),{createInvitation:r,fetching:i}=Ne(),a=j({resolver:V(Re),defaultValues:{email:``,roleId:`employee`}}),o=(0,Z.useCallback)(async t=>{await r(t)&&(e(),n(!1),a.reset())},[r,e,a]);return(0,Q.jsxs)(oe,{open:t,onOpenChange:(0,Z.useCallback)(e=>{n(e),e||a.reset()},[a]),children:[(0,Q.jsx)(y,{asChild:!0,children:(0,Q.jsxs)(A,{children:[(0,Q.jsx)(_e,{className:`size-4`}),`Invite User`]})}),(0,Q.jsxs)(v,{children:[(0,Q.jsxs)(h,{children:[(0,Q.jsx)(x,{children:`Invite User`}),(0,Q.jsx)(w,{children:`Send an invitation to join this business with the selected role.`})]}),(0,Q.jsx)(L,{...a,children:(0,Q.jsxs)(`form`,{onSubmit:a.handleSubmit(o),className:`flex flex-col gap-4`,children:[(0,Q.jsx)(G,{control:a.control,name:`email`,render:({field:e})=>(0,Q.jsxs)(P,{children:[(0,Q.jsx)(z,{children:`Email`}),(0,Q.jsx)(N,{children:(0,Q.jsx)(C,{type:`email`,placeholder:`user@example.com`,...e})}),(0,Q.jsx)(R,{})]})}),(0,Q.jsx)(G,{control:a.control,name:`roleId`,render:({field:e})=>(0,Q.jsxs)(P,{children:[(0,Q.jsx)(z,{children:`Role`}),(0,Q.jsxs)(se,{onValueChange:e.onChange,value:e.value,children:[(0,Q.jsx)(N,{children:(0,Q.jsx)(_,{children:(0,Q.jsx)(ae,{placeholder:`Select a role`})})}),(0,Q.jsx)(b,{children:Ie.map(e=>(0,Q.jsx)(S,{value:e.value,children:e.label},e.value))})]}),(0,Q.jsx)(R,{})]})}),(0,Q.jsx)(p,{children:(0,Q.jsxs)(A,{type:`submit`,disabled:i,children:[i&&(0,Q.jsx)(m,{className:`size-4 animate-spin`}),`Send Invitation`]})})]})})]})]})}function Ve(){let{businessId:e}=ie(),{userContext:t}=(0,Z.useContext)(me),n=t?.context.adminBusinessId;return!n||n!==e?(0,Q.jsx)(ne,{to:re.HOME,replace:!0}):(0,Q.jsxs)(`div`,{className:`flex flex-col gap-4 p-4`,children:[(0,Q.jsx)(`h1`,{className:`text-2xl font-semibold`,children:`Access Management`}),(0,Q.jsxs)(le,{defaultValue:`api-keys`,className:`w-full`,children:[(0,Q.jsxs)(de,{children:[(0,Q.jsx)(O,{value:`api-keys`,children:`API Keys`}),(0,Q.jsx)(O,{value:`invitations`,children:`Invitations`}),(0,Q.jsx)(O,{value:`users`,children:`Users`})]}),(0,Q.jsx)(E,{value:`api-keys`,children:(0,Q.jsx)(we,{})}),(0,Q.jsx)(E,{value:`invitations`,children:(0,Q.jsx)(ze,{})}),(0,Q.jsx)(E,{value:`users`,children:(0,Q.jsx)(Ae,{})})]})]})}export{Ve as AuthManagement};