|  | @@ -1,5 +1,8 @@
 | 
	
		
			
				|  |  |  <script setup lang="ts">
 | 
	
		
			
				|  |  |  import type { Cert } from '@/api/cert'
 | 
	
		
			
				|  |  | +import { CopyOutlined } from '@ant-design/icons-vue'
 | 
	
		
			
				|  |  | +import { useClipboard } from '@vueuse/core'
 | 
	
		
			
				|  |  | +import { message } from 'ant-design-vue'
 | 
	
		
			
				|  |  |  import NodeSelector from '@/components/NodeSelector'
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  interface Props {
 | 
	
	
		
			
				|  | @@ -12,6 +15,23 @@ defineProps<Props>()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // Use defineModel for two-way binding
 | 
	
		
			
				|  |  |  const data = defineModel<Cert>('data', { required: true })
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +const { copy } = useClipboard()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +async function copyToClipboard(text: string, label: string) {
 | 
	
		
			
				|  |  | +  if (!text) {
 | 
	
		
			
				|  |  | +    message.warning($gettext('Nothing to copy'))
 | 
	
		
			
				|  |  | +    return
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  try {
 | 
	
		
			
				|  |  | +    await copy(text)
 | 
	
		
			
				|  |  | +    message.success($gettext(`{label} copied to clipboard`).replace('{label}', label))
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  catch (error) {
 | 
	
		
			
				|  |  | +    console.error(error)
 | 
	
		
			
				|  |  | +    message.error($gettext('Failed to copy to clipboard'))
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  </script>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  <template>
 | 
	
	
		
			
				|  | @@ -26,13 +46,31 @@ const data = defineModel<Cert>('data', { required: true })
 | 
	
		
			
				|  |  |          ? $gettext('This field is required')
 | 
	
		
			
				|  |  |          : ''"
 | 
	
		
			
				|  |  |      >
 | 
	
		
			
				|  |  | -      <p v-if="isManaged">
 | 
	
		
			
				|  |  | -        {{ data.name }}
 | 
	
		
			
				|  |  | -      </p>
 | 
	
		
			
				|  |  | -      <AInput
 | 
	
		
			
				|  |  | -        v-else
 | 
	
		
			
				|  |  | -        v-model:value="data.name"
 | 
	
		
			
				|  |  | -      />
 | 
	
		
			
				|  |  | +      <div v-if="isManaged" class="copy-container">
 | 
	
		
			
				|  |  | +        <p class="copy-text">
 | 
	
		
			
				|  |  | +          {{ data.name }}
 | 
	
		
			
				|  |  | +        </p>
 | 
	
		
			
				|  |  | +        <AButton
 | 
	
		
			
				|  |  | +          v-if="data.name"
 | 
	
		
			
				|  |  | +          type="text"
 | 
	
		
			
				|  |  | +          size="small"
 | 
	
		
			
				|  |  | +          @click="copyToClipboard(data.name, $gettext('Name'))"
 | 
	
		
			
				|  |  | +        >
 | 
	
		
			
				|  |  | +          <CopyOutlined />
 | 
	
		
			
				|  |  | +        </AButton>
 | 
	
		
			
				|  |  | +      </div>
 | 
	
		
			
				|  |  | +      <div v-else class="input-with-copy">
 | 
	
		
			
				|  |  | +        <AInput v-model:value="data.name" />
 | 
	
		
			
				|  |  | +        <AButton
 | 
	
		
			
				|  |  | +          v-if="data.name"
 | 
	
		
			
				|  |  | +          type="text"
 | 
	
		
			
				|  |  | +          size="small"
 | 
	
		
			
				|  |  | +          class="copy-button"
 | 
	
		
			
				|  |  | +          @click="copyToClipboard(data.name, $gettext('Name'))"
 | 
	
		
			
				|  |  | +        >
 | 
	
		
			
				|  |  | +          <CopyOutlined />
 | 
	
		
			
				|  |  | +        </AButton>
 | 
	
		
			
				|  |  | +      </div>
 | 
	
		
			
				|  |  |      </AFormItem>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      <AFormItem
 | 
	
	
		
			
				|  | @@ -42,13 +80,31 @@ const data = defineModel<Cert>('data', { required: true })
 | 
	
		
			
				|  |  |          : errors.ssl_certificate_path === 'certificate_path'
 | 
	
		
			
				|  |  |            ? $gettext('The path exists, but the file is not a certificate') : ''"
 | 
	
		
			
				|  |  |      >
 | 
	
		
			
				|  |  | -      <p v-if="isManaged">
 | 
	
		
			
				|  |  | -        {{ data.ssl_certificate_path }}
 | 
	
		
			
				|  |  | -      </p>
 | 
	
		
			
				|  |  | -      <AInput
 | 
	
		
			
				|  |  | -        v-else
 | 
	
		
			
				|  |  | -        v-model:value="data.ssl_certificate_path"
 | 
	
		
			
				|  |  | -      />
 | 
	
		
			
				|  |  | +      <div v-if="isManaged" class="copy-container">
 | 
	
		
			
				|  |  | +        <p class="copy-text">
 | 
	
		
			
				|  |  | +          {{ data.ssl_certificate_path }}
 | 
	
		
			
				|  |  | +        </p>
 | 
	
		
			
				|  |  | +        <AButton
 | 
	
		
			
				|  |  | +          v-if="data.ssl_certificate_path"
 | 
	
		
			
				|  |  | +          type="text"
 | 
	
		
			
				|  |  | +          size="small"
 | 
	
		
			
				|  |  | +          @click="copyToClipboard(data.ssl_certificate_path, $gettext('SSL Certificate Path'))"
 | 
	
		
			
				|  |  | +        >
 | 
	
		
			
				|  |  | +          <CopyOutlined />
 | 
	
		
			
				|  |  | +        </AButton>
 | 
	
		
			
				|  |  | +      </div>
 | 
	
		
			
				|  |  | +      <div v-else class="input-with-copy">
 | 
	
		
			
				|  |  | +        <AInput v-model:value="data.ssl_certificate_path" />
 | 
	
		
			
				|  |  | +        <AButton
 | 
	
		
			
				|  |  | +          v-if="data.ssl_certificate_path"
 | 
	
		
			
				|  |  | +          type="text"
 | 
	
		
			
				|  |  | +          size="small"
 | 
	
		
			
				|  |  | +          class="copy-button"
 | 
	
		
			
				|  |  | +          @click="copyToClipboard(data.ssl_certificate_path, $gettext('SSL Certificate Path'))"
 | 
	
		
			
				|  |  | +        >
 | 
	
		
			
				|  |  | +          <CopyOutlined />
 | 
	
		
			
				|  |  | +        </AButton>
 | 
	
		
			
				|  |  | +      </div>
 | 
	
		
			
				|  |  |      </AFormItem>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      <AFormItem
 | 
	
	
		
			
				|  | @@ -58,13 +114,31 @@ const data = defineModel<Cert>('data', { required: true })
 | 
	
		
			
				|  |  |          : errors.ssl_certificate_key_path === 'privatekey_path'
 | 
	
		
			
				|  |  |            ? $gettext('The path exists, but the file is not a private key') : ''"
 | 
	
		
			
				|  |  |      >
 | 
	
		
			
				|  |  | -      <p v-if="isManaged">
 | 
	
		
			
				|  |  | -        {{ data.ssl_certificate_key_path }}
 | 
	
		
			
				|  |  | -      </p>
 | 
	
		
			
				|  |  | -      <AInput
 | 
	
		
			
				|  |  | -        v-else
 | 
	
		
			
				|  |  | -        v-model:value="data.ssl_certificate_key_path"
 | 
	
		
			
				|  |  | -      />
 | 
	
		
			
				|  |  | +      <div v-if="isManaged" class="copy-container">
 | 
	
		
			
				|  |  | +        <p class="copy-text">
 | 
	
		
			
				|  |  | +          {{ data.ssl_certificate_key_path }}
 | 
	
		
			
				|  |  | +        </p>
 | 
	
		
			
				|  |  | +        <AButton
 | 
	
		
			
				|  |  | +          v-if="data.ssl_certificate_key_path"
 | 
	
		
			
				|  |  | +          type="text"
 | 
	
		
			
				|  |  | +          size="small"
 | 
	
		
			
				|  |  | +          @click="copyToClipboard(data.ssl_certificate_key_path, $gettext('SSL Certificate Key Path'))"
 | 
	
		
			
				|  |  | +        >
 | 
	
		
			
				|  |  | +          <CopyOutlined />
 | 
	
		
			
				|  |  | +        </AButton>
 | 
	
		
			
				|  |  | +      </div>
 | 
	
		
			
				|  |  | +      <div v-else class="input-with-copy">
 | 
	
		
			
				|  |  | +        <AInput v-model:value="data.ssl_certificate_key_path" />
 | 
	
		
			
				|  |  | +        <AButton
 | 
	
		
			
				|  |  | +          v-if="data.ssl_certificate_key_path"
 | 
	
		
			
				|  |  | +          type="text"
 | 
	
		
			
				|  |  | +          size="small"
 | 
	
		
			
				|  |  | +          class="copy-button"
 | 
	
		
			
				|  |  | +          @click="copyToClipboard(data.ssl_certificate_key_path, $gettext('SSL Certificate Key Path'))"
 | 
	
		
			
				|  |  | +        >
 | 
	
		
			
				|  |  | +          <CopyOutlined />
 | 
	
		
			
				|  |  | +        </AButton>
 | 
	
		
			
				|  |  | +      </div>
 | 
	
		
			
				|  |  |      </AFormItem>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      <AFormItem :label="$gettext('Sync to')">
 | 
	
	
		
			
				|  | @@ -77,4 +151,29 @@ const data = defineModel<Cert>('data', { required: true })
 | 
	
		
			
				|  |  |  </template>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  <style scoped lang="less">
 | 
	
		
			
				|  |  | +.copy-container {
 | 
	
		
			
				|  |  | +  display: flex;
 | 
	
		
			
				|  |  | +  align-items: center;
 | 
	
		
			
				|  |  | +  gap: 8px;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  .copy-text {
 | 
	
		
			
				|  |  | +    margin: 0;
 | 
	
		
			
				|  |  | +    flex: 1;
 | 
	
		
			
				|  |  | +    word-break: break-all;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +.input-with-copy {
 | 
	
		
			
				|  |  | +  display: flex;
 | 
	
		
			
				|  |  | +  align-items: center;
 | 
	
		
			
				|  |  | +  gap: 8px;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  .ant-input {
 | 
	
		
			
				|  |  | +    flex: 1;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  .copy-button {
 | 
	
		
			
				|  |  | +    flex-shrink: 0;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  </style>
 |