diff --git a/web/src/components/domains/DomainInfo.css b/web/src/components/domains/DomainInfo.css
new file mode 100644
index 0000000..9acbeca
--- /dev/null
+++ b/web/src/components/domains/DomainInfo.css
@@ -0,0 +1,11 @@
+div {
+ display: block;
+}
+
+span {
+ padding-left: 0.5em;
+}
+
+.icon {
+ transform: translateY(2px);
+}
\ No newline at end of file
diff --git a/web/src/components/domains/DomainInfo.tsx b/web/src/components/domains/DomainInfo.tsx
new file mode 100644
index 0000000..ce8c302
--- /dev/null
+++ b/web/src/components/domains/DomainInfo.tsx
@@ -0,0 +1,27 @@
+import './DomainInfo.css'
+
+import { type Domain } from "../../stores/domains";
+import { NIcon } from "naive-ui";
+import { AddressCard, Server } from "@vicons/fa";
+import { defineComponent, defineProps } from "vue";
+
+type Props = {
+ domain: Domain
+}
+
+function DomainInfo({domain}: Props) {
+ return (
+
+
+
+ {domain.admin_email}
+
+
+
+ {domain.main_dns}
+
+
+ )
+}
+
+export default DomainInfo
\ No newline at end of file
diff --git a/web/src/components/domains/DomainInfo.vue b/web/src/components/domains/DomainInfo.vue
deleted file mode 100644
index 5b68671..0000000
--- a/web/src/components/domains/DomainInfo.vue
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
- {{ domain.admin_email }}
-
-
-
- {{ domain.main_dns }}
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/web/src/components/domains/DomainOps.tsx b/web/src/components/domains/DomainOps.tsx
new file mode 100644
index 0000000..3a71d01
--- /dev/null
+++ b/web/src/components/domains/DomainOps.tsx
@@ -0,0 +1,79 @@
+import { NSpace, NButton, NIcon, NTooltip, NFlex } from 'naive-ui'
+import { TrashAlt, EditRegular, Book } from '@vicons/fa'
+import { type Domain } from "../../stores/domains"
+import router from '@/router';
+import i18n from '@/locale/i18n'
+import type { SetupContext } from 'vue';
+const t = i18n.global.t
+
+type Props = {
+ domain: Domain
+}
+
+type Events = {
+ removeDomain(domain: Domain): void
+ editDomain(domain: Domain): void
+}
+
+function loadRecord({ domain }: Props) {
+ return (
+
+ {{
+ trigger: () =>
+ { router.push(`/records/${domain.domain_name}`) }}>
+ {{ icon: () => }}
+ ,
+ default: () => t('domains.dnsRecord')
+ }}
+
+ )
+}
+
+function editDomain({ domain }: Props, { emit }: SetupContext) {
+ return (
+
+ {{
+ default: () => t('common.edit'),
+ trigger: () =>
+ emit("editDomain", domain)}>
+ {{
+ icon: () =>
+
+ }}
+
+ }}
+
+
+ )
+}
+
+function deleteDomain({ domain }: Props, { emit }: SetupContext) {
+ return (
+
+ {{
+ default: () => t('common.delete'),
+ trigger: () =>
+ emit("removeDomain", domain)}>
+ {{
+ icon: () =>
+
+ }}
+
+ }}
+
+ )
+}
+
+function DomainOps({ domain }: Props, { emit }: SetupContext) {
+ return (
+
+
+
+ emit("editDomain", d)} />
+ emit("removeDomain", d)} />
+
+
+ )
+}
+
+export default DomainOps
\ No newline at end of file
diff --git a/web/src/components/domains/DomainOps.vue b/web/src/components/domains/DomainOps.vue
deleted file mode 100644
index bac8022..0000000
--- a/web/src/components/domains/DomainOps.vue
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- {{ t('domains.dnsRecord') }}
-
-
-
-
-
-
-
-
-
- {{ t('common.edit') }}
-
-
-
-
-
-
-
-
-
- {{ t('common.delete') }}
-
-
-
-
-
-
\ No newline at end of file
diff --git a/web/src/components/domains/DomainRemoveModal.css b/web/src/components/domains/DomainRemoveModal.css
new file mode 100644
index 0000000..cd472b0
--- /dev/null
+++ b/web/src/components/domains/DomainRemoveModal.css
@@ -0,0 +1,7 @@
+.icon-down {
+ transform: translateY(2px);
+}
+
+b#boldit {
+ font-weight: bold;
+}
\ No newline at end of file
diff --git a/web/src/components/domains/DomainRemoveModal.tsx b/web/src/components/domains/DomainRemoveModal.tsx
new file mode 100644
index 0000000..0372ba1
--- /dev/null
+++ b/web/src/components/domains/DomainRemoveModal.tsx
@@ -0,0 +1,90 @@
+import './DomainRemoveModal.css'
+
+import { useDomainStore, type Domain } from '@/stores/domains';
+import { NModal, NCard, NFlex, NButton, NIcon, NInput, useNotification } from 'naive-ui'
+import { Times, TrashAlt, QuestionCircle } from '@vicons/fa';
+import { getErrorInfo } from '@/apis/api';
+import i18n from '@/locale/i18n';
+import type { EmitsOptions, ObjectEmitsOptions, SetupContext } from 'vue';
+
+const t = i18n.global.t
+const domainStore = useDomainStore()
+const notification = useNotification()
+
+type Props = {
+ domain: Domain
+ show: boolean
+}
+
+type Events = {
+ 'update:show': (value: boolean) => void
+}
+
+let domain_name = ''
+let loading = false
+
+async function confirm(domain: Domain) {
+ domain_name = ''
+ loading = true
+
+ try {
+ if (domain)
+ await domainStore.removeDomain(domain)
+ } catch (e) {
+ const msg = getErrorInfo(e)
+ notification.error(msg)
+ console.error(e)
+ } finally {
+ loading = false
+ }
+}
+
+function modalBody({ domain }: Props) {
+ return (
+ <>
+ {t('common.deleteConfirm')}
+ {t('domains.deleteHint')}
+ {t('domains.confirm1')} {domain.domain_name} {t('domains.confirm2')}
+
+
+ domain_name = v} placeholder={domain.domain_name} />
+
+ >
+ )
+}
+
+function modalActions({ domain }: Props, { emit }: SetupContext) {
+ return <>
+
+ { emit('update:show', false) }}>
+ {{
+ icon: () => ,
+ default: () => t('common.cancel')
+ }}
+
+
+ confirm(domain)}>
+ {{
+ icon: () => ,
+ default: () => t('common.confirm')
+ }}
+
+
+ >
+}
+
+function DomainRemoveModal({ domain, show }: Props, { emit }: SetupContext) {
+ return (
+
+
+ {{
+ header: () => <>{t('domains.delete')} - {domain.domain_name}>,
+ default: () => ,
+ action: () => emit('update:show', v)} />
+ }}
+
+
+ )
+}
+
+export default DomainRemoveModal
\ No newline at end of file
diff --git a/web/src/components/domains/DomainRemoveModal.vue b/web/src/components/domains/DomainRemoveModal.vue
deleted file mode 100644
index 6cda319..0000000
--- a/web/src/components/domains/DomainRemoveModal.vue
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-
-
-
-
-
- {{ t('domains.delete') }} - {{ domain?.domain_name }}
-
- {{ t('common.deleteConfirm') }}
- {{ t('domains.deleteHint') }}
- {{ t('domains.confirm1') }} {{ domain?.domain_name }} {{ t('domains.confirm2') }}
-
-
-
-
-
-
-
-
-
-
-
-
- {{ t('common.cancel') }}
-
-
-
-
-
-
-
-
- {{ t('common.confirm') }}
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/web/src/views/DomainsView.vue b/web/src/views/DomainsView.vue
index 9a1b76f..e9f526f 100644
--- a/web/src/views/DomainsView.vue
+++ b/web/src/views/DomainsView.vue
@@ -4,9 +4,9 @@ import { PlusSquare } from "@vicons/fa"
import { onMounted, ref } from 'vue'
import { type Domain, useDomainStore } from '@/stores/domains'
import { getErrorInfo } from '@/apis/api'
-import DomainInfo from '@/components/domains/DomainInfo.vue'
-import DomainOps from '@/components/domains/DomainOps.vue'
-import DomainRemoveModal from '@/components/domains/DomainRemoveModal.vue'
+import DomainInfo from '@/components/domains/DomainInfo'
+import DomainOps from '@/components/domains/DomainOps'
+import DomainRemoveModal from '@/components/domains/DomainRemoveModal'
import DomainEditModal from '@/components/domains/DomainEditModal.vue'
const domainStore = useDomainStore()