@@ -38,6 +38,7 @@ export function CompanySettingsForm({ company }: CompanySettingsFormProps) {
3838 const router = useRouter ( )
3939 const queryClient = useQueryClient ( )
4040 const [ deleteConfirmation , setDeleteConfirmation ] = useState ( "" )
41+ const [ copyStatus , setCopyStatus ] = useState < "idle" | "copied" | "error" > ( "idle" )
4142 const updateMutation = useMutation ( {
4243 mutationFn : ( payload : { name : string ; description : string } ) =>
4344 updateCompanyAction ( company . id , payload ) . then ( ( result ) => {
@@ -76,8 +77,37 @@ export function CompanySettingsForm({ company }: CompanySettingsFormProps) {
7677
7778 return result
7879 } ) ,
80+ onSuccess : ( ) => {
81+ setCopyStatus ( "idle" )
82+ } ,
7983 } )
8084
85+ async function copyToken ( token : string ) {
86+ try {
87+ if ( navigator . clipboard ?. writeText ) {
88+ await navigator . clipboard . writeText ( token )
89+ } else {
90+ const textArea = document . createElement ( "textarea" )
91+ textArea . value = token
92+ textArea . setAttribute ( "readonly" , "" )
93+ textArea . style . position = "fixed"
94+ textArea . style . opacity = "0"
95+ document . body . appendChild ( textArea )
96+ textArea . select ( )
97+ const copied = document . execCommand ( "copy" )
98+ document . body . removeChild ( textArea )
99+
100+ if ( ! copied ) {
101+ throw new Error ( "Copy command failed" )
102+ }
103+ }
104+
105+ setCopyStatus ( "copied" )
106+ } catch {
107+ setCopyStatus ( "error" )
108+ }
109+ }
110+
81111 function updateCompany ( formData : FormData ) {
82112 updateMutation . mutate ( {
83113 name : String ( formData . get ( "name" ) ?? "" ) ,
@@ -130,10 +160,33 @@ export function CompanySettingsForm({ company }: CompanySettingsFormProps) {
130160 </ p >
131161 { tokenMutation . data ?. token ? (
132162 < div className = "mt-4 rounded-2xl border border-border bg-muted p-3" >
133- < p className = "text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground" >
134- New company bearer token · copy now
135- </ p >
136- < p className = "mt-2 break-all font-mono text-xs" > { tokenMutation . data . token } </ p >
163+ < div className = "flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between" >
164+ < div >
165+ < p className = "text-xs font-medium uppercase tracking-[0.16em] text-muted-foreground" >
166+ New company bearer token · copy now
167+ </ p >
168+ < p className = "mt-2 break-all font-mono text-xs" > { tokenMutation . data . token } </ p >
169+ </ div >
170+ < Button
171+ className = "shrink-0"
172+ size = "sm"
173+ type = "button"
174+ variant = "secondary"
175+ onClick = { ( ) => copyToken ( tokenMutation . data . token ) }
176+ >
177+ { copyStatus === "copied" ? "Copied" : "Copy token" }
178+ </ Button >
179+ </ div >
180+ { copyStatus === "copied" ? (
181+ < p className = "mt-2 text-xs font-medium text-emerald-700 dark:text-emerald-300" >
182+ Token copied to clipboard.
183+ </ p >
184+ ) : null }
185+ { copyStatus === "error" ? (
186+ < p className = "mt-2 text-xs font-medium text-destructive" >
187+ Could not copy automatically. Select and copy the token manually.
188+ </ p >
189+ ) : null }
137190 < p className = "mt-2 text-xs text-muted-foreground" >
138191 This token cannot be viewed again. Update `.openclaw/agentbridge/.env` or any other
139192 external agent configuration that uses the old token.
0 commit comments