diff --git a/management-ui/src/customcompometns/AdminDeviceDropdown.vue b/management-ui/src/customcompometns/AdminDeviceDropdown.vue
index 3136ebe..062c193 100644
--- a/management-ui/src/customcompometns/AdminDeviceDropdown.vue
+++ b/management-ui/src/customcompometns/AdminDeviceDropdown.vue
@@ -9,22 +9,36 @@ import DeleteDeviceDialog from './DeleteDeviceDialog.vue'
import DeviceCertificateDialog from './DeviceCertificateDialog.vue'
import DeviceTasksDialog from './DeviceTasksDialog.vue'
import { Ellipsis } from 'lucide-vue-next'
-import type { Device } from '@/lib/interfaces'
+import type { Device, Users } from '@/lib/interfaces'
// import { api } from '@/lib/api'
-const props = defineProps<{ row: Device }>() // ← accept full row
+const props = defineProps<{
+ row: Device
+ allUsers?: Users[]
+ onRowUpdated?: (row: Device, payload: { name: string; userIds: string[] }) => void
+ onRowDeleted?: (row: Device) => void
+}>() // ← accept full row
const isEditOpen = ref(false)
const isDeleteOpen = ref(false)
const isTasksOpen = ref(false)
const itCertsOpen = ref(false)
+const emit = defineEmits<{
+ (e: 'refresh'): void
+ (e: 'error', err: unknown): void
+}>()
+
function onDeleteConfirmed() {
// await api.delete(`/devices/${encodeURIComponent(props.row.guid)}`)
isDeleteOpen.value = false
+ // emit('refresh')
+ props.onRowDeleted?.(props.row)
}
-function onEditConfirm() {
+function onEditConfirm(_payload: { name: string; userIds: string[] }) {
isEditOpen.value = false
+ // emit('refresh')
+ props.onRowUpdated?.(props.row, _payload)
}
function onTaskConfirm() {
@@ -47,11 +61,11 @@ function onCertsConfirm() {
Rename
Tasks
Certificates
- Delete
+ Delete
-
+
diff --git a/management-ui/src/customcompometns/Admincomponent.vue b/management-ui/src/customcompometns/Admincomponent.vue
index f9f3e99..adec5d7 100644
--- a/management-ui/src/customcompometns/Admincomponent.vue
+++ b/management-ui/src/customcompometns/Admincomponent.vue
@@ -1,38 +1,33 @@
-
-
-
+
+
+
-
-
-
- Users
-
-
- Devices
-
-
- Trackers
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+ Users
+ Devices
+ Trackers
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/management-ui/src/customcompometns/DataTableNoCheckbox.vue b/management-ui/src/customcompometns/DataTableNoCheckbox.vue
index eb8f3cd..6602884 100644
--- a/management-ui/src/customcompometns/DataTableNoCheckbox.vue
+++ b/management-ui/src/customcompometns/DataTableNoCheckbox.vue
@@ -33,7 +33,13 @@ const props = defineProps<{
* :dropdownComponent="MyActionsMenu"
* />
*/
- dropdownComponent?: DefineComponent<{ row: TData }, any, any>
+ // dropdownComponent?: DefineComponent<{ row: TData }, any, any>
+ dropdownComponent?: DefineComponent<{ row: TData } & {
+ onRowUpdated?: (row: TData, payload: any) => void
+ onRowDeleted?: (row: TData) => void
+ }, any, any>
+ onRowUpdated?: (row: TData, payload: any) => void // <-- NEW
+ onRowDeleted?: (row: TData) => void // <-- NEW
}>()
// ——— Table setup ———
@@ -97,6 +103,8 @@ const table = useVueTable({
:is="props.dropdownComponent"
:row="row.original"
:key="row.id"
+ :on-row-updated="props.onRowUpdated"
+ :on-row-deleted="props.onRowDeleted"
/>
diff --git a/management-ui/src/customcompometns/DeviceCertificateDialog.vue b/management-ui/src/customcompometns/DeviceCertificateDialog.vue
index 1b69d90..847681c 100644
--- a/management-ui/src/customcompometns/DeviceCertificateDialog.vue
+++ b/management-ui/src/customcompometns/DeviceCertificateDialog.vue
@@ -37,7 +37,7 @@ function onSave() {
List of certificates
- List of certificates for device {{ props.device?.guid }}
+ {{ props.device?.guid }}
diff --git a/management-ui/src/customcompometns/DeviceTasksDialog.vue b/management-ui/src/customcompometns/DeviceTasksDialog.vue
index 39ec1bf..4f1c8e0 100644
--- a/management-ui/src/customcompometns/DeviceTasksDialog.vue
+++ b/management-ui/src/customcompometns/DeviceTasksDialog.vue
@@ -35,7 +35,7 @@ function onSave() {
Tasks
- List of tasks for device {{ props.device?.guid }}
+ {{ props.device?.guid }}
diff --git a/management-ui/src/customcompometns/EditDeviceDialog.vue b/management-ui/src/customcompometns/EditDeviceDialog.vue
index 61ece79..ce70dec 100644
--- a/management-ui/src/customcompometns/EditDeviceDialog.vue
+++ b/management-ui/src/customcompometns/EditDeviceDialog.vue
@@ -10,76 +10,111 @@ import {
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
-import { defineProps, defineEmits, ref, watch } from 'vue'
+import { defineProps, defineEmits, ref, watch, computed } from 'vue'
import type { PropType } from 'vue'
import AssignDevice from './AssignDevice.vue'
import type { Device, Users } from '@/lib/interfaces'
+import { api } from '@/lib/api'
+
+
+type DeviceWithUsers =
+ & Partial
+ & { guid?: string; name?: string; devicename?: string }
+ & { users?: Array<{ id: number; username: string; role?: string }> }
// 1) runtime props so Vue + TS agree
const props = defineProps({
- modelValue: {
- type: Boolean as PropType,
- required: true,
- },
- device: { type: Object as PropType, required: false },
- allUsers: { type: Array as PropType, required: false },
- initialUserIds: { type: Array as PropType, required: false }, // <- if you have IDs already
+ modelValue: { type: Boolean as PropType, required: true },
+ device: { type: Object as PropType, required: true }, // must have guid
+ allUsers: { type: Array as PropType, required: false },
})
// 2) two emits: v-model and confirm
const emit = defineEmits<{
(e: 'update:modelValue', v: boolean): void
- (e: 'confirm'): void
+ // (e: 'confirm'): void
+ (e: 'updated', payload: { name: string; userIds: string[] }): void
}>()
+// ---------- Local form state ----------
+const guid = computed(() => String(props.device?.guid ?? ''))
+const originalName = computed(() => (props.device?.devicename ?? props.device?.name ?? ''))
+const originalIds = computed(() =>
+ Array.isArray(props.device?.users) ? props.device!.users.map(u => String(u.id)) : []
+)
+
const name = ref('')
const selectedUserIds = ref([])
-// helper: map usernames → ids when we only have a string of usernames
-const usernameToId = (uname: string): string | null => {
- const id = props.allUsers?.find(u => u.username === uname)?.id
- return typeof id === 'number' ? String(id) : null
-}
-
-
-// when device changes or dialog opens, update local value
watch(
() => props.device,
- (dev) => {
- console.log(dev?.assigned_users)
- name.value = dev?.devicename ?? ''
- if (props.initialUserIds && props.initialUserIds.length) {
- selectedUserIds.value = [...props.initialUserIds]
- return
- }
- const usernames = (dev?.assigned_users ?? '')
- .split(',')
- .map(s => s.trim())
- .filter(Boolean)
-
- if (usernames.length && props.allUsers?.length) {
- selectedUserIds.value = usernames
- .map(usernameToId)
- .filter((x): x is string => !!x)
- } else {
- selectedUserIds.value = []
- }
+ () => {
+ name.value = originalName.value
+ selectedUserIds.value = [...originalIds.value]
},
{ immediate: true }
)
-function onSave() {
- emit('confirm')
- // close the dialog
- emit('update:modelValue', false)
+// ---------- Save (conditional API calls) ----------
+const saving = ref(false)
+const errorText = ref(null)
+
+function changedName() {
+ return name.value.trim() !== originalName.value.trim()
+}
+function changedUsers() {
+ // compare as sets
+ const a = new Set(originalIds.value)
+ const b = new Set(selectedUserIds.value)
+ if (a.size !== b.size) return true
+ for (const id of a) if (!b.has(id)) return true
+ return false
+}
+
+async function onSave() {
+ if (!guid.value) return
+ errorText.value = null
+ saving.value = true
+ try {
+ const ops: Promise[] = []
+
+ if (changedName()) {
+ ops.push(
+ api.post(`/devices/${encodeURIComponent(guid.value)}/rename`, {
+ name: name.value.trim(),
+ } as { name: string })
+ )
+ }
+
+ if (changedUsers()) {
+ const userIdsNum = selectedUserIds.value
+ .map(v => Number(v))
+ .filter(n => Number.isFinite(n)) as number[]
+
+ ops.push(
+ api.post(`/devices/${encodeURIComponent(guid.value)}/set_users`, {
+ userIds: userIdsNum,
+ } as { userIds: number[] })
+ )
+ }
+
+ if (ops.length > 0) {
+ await Promise.all(ops)
+ }
+
+ emit('updated', { name: name.value, userIds: [...selectedUserIds.value] })
+ emit('update:modelValue', false)
+ } catch (err: any) {
+ console.error(err)
+ errorText.value = err?.response?.data?.message || 'Failed to save changes.'
+ } finally {
+ saving.value = false
+ }
}
-