Implement routes and transitions

This commit is contained in:
2025-07-14 12:08:09 +02:00
parent 736dad748b
commit 5ac4c987d3
27 changed files with 860 additions and 376 deletions

View File

@ -0,0 +1,24 @@
<template>
<ClientDetailView :id="clientId" :parentId="parentId" @clientDeleted="onClientDelete" />
</template>
<script setup lang="ts">
import { toRef } from 'vue'
import ClientDetailView from '@/views/clients/ClientDetailView.vue'
import { useRouter, useRoute } from 'vue-router'
import { useTreeState } from '@/store/useTreeState'
const props = defineProps<{ id: string | null; parentId: string | null }>()
const router = useRouter()
const clientId = toRef(() => props.id)
const parentId = toRef(() => props.parentId)
const treeState = useTreeState()
async function onClientDelete(id: string) {
// React when a client is deleted
console.log('Client deleted')
await treeState.loadClients()
router.push({ name: 'clients' })
}
</script>

View File

@ -1,44 +1,54 @@
<template>
<ClientDetail :client="client" @update="updateClient" @deleted="deleteClient" v-if="client" />
<ClientDetail
:client="client"
@update="updateClient"
@deleted="deleteClient"
v-if="client"
:key="clientId"
/>
<ClientSkeleton v-else />
</template>
<script setup lang="ts">
import { ref, watch, onMounted } from 'vue'
import { ref, toRef, watch, onMounted } from 'vue'
import ClientSkeleton from '@/components/clients/ClientSkeleton.vue'
import ClientDetail from '@/components/clients/ClientDetail.vue'
import type { ClientCreate } from '@/client'
import { idKey } from '@/api/paths'
import { SshecretAdmin } from '@/client'
import { useTreeState } from '@/store/useTreeState'
const props = defineProps<{ clientId: string | null }>()
const props = defineProps<{ id: string | null; parentId: string | null }>()
const clientId = toRef(() => props.id)
const client = ref<Client>()
const treeState = useTreeState()
const emit = defineEmits<{ (e: 'clientDeleted', data: string): void }>()
async function loadClient() {
if (!props.clientId) return
client.value = await treeState.getClient()
console.log('loadClient called: ', props.id)
if (!props.id) return
client.value = await treeState.getClient(props.id)
}
async function deleteClient(clientId: string) {
console.log(`Delete ${localClient.value.id}`)
async function deleteClient(deleteId: string) {
const response = await SshecretAdmin.deleteClientApiV1ClientsIdDelete({
path: { id: clientId },
path: { id: idKey(deleteId) },
})
if (response.status !== 200) {
console.error(response)
return
}
emit('clientDeleted', clientId)
props.clientId = null
emit('clientDeleted', deleteId)
}
async function updateClient(updated: ClientCreate) {
const response = await SshecretAdmin.updateClientApiV1ClientsIdPut({
path: { id: localClient.value.id },
path: { id: idKey(localClient.value.id) },
body: data,
})
client.value = response.data
@ -47,7 +57,7 @@ async function updateClient(updated: ClientCreate) {
onMounted(loadClient)
watch(
() => props.client_id,
() => props.id,
() => loadClient(),
{ immediate: true },
)

View File

@ -0,0 +1,29 @@
<template>
<MasterDetail>
<template #master>
<MasterTabs selectedTab="clients" @change="tabSelected" />
</template>
<template #detail>
<RouterView :key="routeKey" />
</template>
</MasterDetail>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import MasterDetail from '@/views/layout/MasterDetail.vue'
import MasterTabs from '@/components/common/MasterTabs.vue'
import ClientDetailView from '@/views/clients/ClientDetailView.vue'
import { useRouter, useRoute } from 'vue-router'
import { RouterView } from 'vue-router'
const router = useRouter()
const route = useRoute()
function tabSelected(tabName: string) {
if (tabName !== 'clients') {
router.push({ name: tabName })
}
}
const routeKey = computed(() => route.name + '-' + (route.params.id ?? 'root'))
</script>

View File

@ -44,6 +44,11 @@
</ClientTreeItem>
</template>
</sl-tree>
<sl-tree class="w-full" v-else>
<template v-for="n in 20">
<TreeItemSkeleton />
</template>
</sl-tree>
</div>
<div
class="shrink-0 mt-4 pt-2 border-t border-gray-100 dark:border-gray-700 bg-white dark:bg-gray-800"
@ -88,7 +93,7 @@
</template>
<script setup lang="ts">
import { computed, ref, reactive, onMounted, watch } from 'vue'
import { computed, ref, reactive, toRef, onMounted, watch, nextTick } from 'vue'
import type { Ref } from 'vue'
import { usePagination } from '@/composables/usePagination'
@ -98,11 +103,13 @@ import { SshecretAdmin } from '@/client/sdk.gen'
import type { Client, ClientCreate } from '@/client/types.gen'
import { useTreeState } from '@/store/useTreeState'
import { useRouter, useRoute } from 'vue-router'
import ClientTreeItem from '@/components/clients/ClientTreeItem.vue'
import ClientSecretTreeItem from '@/components/clients/ClientSecretTreeItem.vue'
import ClientForm from '@/components/clients/ClientForm.vue'
import PageNumbers from '@/components/common/PageNumbers.vue'
import TreeItemSkeleton from '@/components/common/TreeItemSkeleton.vue'
import { useDebounce } from '@/composables/useDebounce'
const treeState = useTreeState()
@ -117,7 +124,15 @@ const selectedSecret = ref<string | null>(null)
const createFormKey = ref<number>(0)
const createDrawerOpen = ref<boolean>(false)
const clientQuery = ref('')
const props = defineProps({
loadClient: {
type: String,
default: '',
},
})
const router = useRouter()
const clientQuery = toRef(() => props.loadClient)
const debouncedQuery = useDebounce(clientQuery, 300)
@ -134,49 +149,43 @@ async function loadClients() {
function updateClient(updated: Client) {
const index = clients.value.findIndex((c) => c.name === updated.name)
console.log(`UpdateClient fired: ${updated.name} => ${index}`)
if (index >= 0) {
clients.value[index] = updated
}
}
function itemSelected(event: Event) {
if (event.detail.selection.length == 0) {
treeState.unselect()
} else {
if (event.detail.selection) {
const el = event.detail.selection[0] as HTMLElement
const childType = el.dataset.type
if (childType === 'client') {
const clientId = el.dataset.clientId
treeState.selectClient(clientId)
} else if (childType == 'secret') {
const secretName = el.dataset.name
router.push({ name: 'Client', params: { id: el.dataset.clientId } })
} else {
const secretId = el.dataset.name
const parentId = el.dataset.parentId
treeState.selectSecret(secretName, parentId)
console.log(el.dataset)
router.push({
name: 'ClientSecret',
params: { parentId: el.dataset.parentId, id: el.dataset.name },
})
}
}
}
async function createClient(data: ClientCreate) {
const response = await SshecretAdmin.createClientApiV1ClientsPost({ body: data })
console.log(response.data)
clients.value.unshift(response.data)
totalClients.value += 1
createDrawerOpen.value = false
createFormKey.value += 1
treeState.value.selected = true
treeState.value.item_type = 'secret'
treeState.value.client = response.data
treeState.selectClient(response.data.id)
router.push({ name: 'Client', params: { id: response.data.id } })
}
async function clientDeleted(id: string) {
const index = clients.value.findIndex((c) => c.id === id)
console.log(`Client Deleted event received: ID: ${id} => ${index}`)
if (index >= 0) {
clients.value.splice(index, 1)
treeState.value.selected = false
treeState.value.item_type = null
treeState.value.client = null
treeState.unselect()
await loadClients()
}
}
@ -196,7 +205,6 @@ async function clearSearch() {
// Watch the search query
watch(debouncedQuery, async () => {
console.log('Handling search event.')
await handleSearchEvent()
})