From 1c0551a809648b095a88e5716663aaed5cc07f6c Mon Sep 17 00:00:00 2001 From: Viswamedha Nalabotu Date: Sun, 8 Mar 2026 13:19:17 +0000 Subject: [PATCH] Flattened api structure --- site/src/router/api.ts | 41 ++++++------ site/src/views/InviteAccept.vue | 4 +- site/src/views/OrganizationManage.vue | 92 +++++++++++++++++++-------- 3 files changed, 90 insertions(+), 47 deletions(-) diff --git a/site/src/router/api.ts b/site/src/router/api.ts index 3d95c63..dd258fc 100644 --- a/site/src/router/api.ts +++ b/site/src/router/api.ts @@ -93,27 +93,29 @@ export const API = { list: () => 'organization/', byId: (uuid: string) => `organization/${uuid}/`, members: { - list: (uuid: string) => `organization/${uuid}/members/`, - remove: (uuid: string, userUuid: string) => - `organization/${uuid}/member/${userUuid}/remove/`, - }, - invites: { - list: (uuid: string) => `organization/${uuid}/invite/`, - create: (uuid: string, maxUses: number) => - `organization/${uuid}/create-invite/?max_uses=${maxUses}`, - revoke: (uuid: string, inviteUuid: string) => - `organization/${uuid}/revoke-invite/${inviteUuid}/`, - join: (inviteUuid: string) => `organization/join/${inviteUuid}/`, + list: (organizationUuid: string) => `organization/${organizationUuid}/members/`, + remove: (organizationUuid: string, userUuid: string) => + `organization/${organizationUuid}/member/${userUuid}/remove/`, }, leave: (uuid: string) => `organization/${uuid}/leave/`, - roles: { - list: (uuid: string) => `organization/${uuid}/role/`, - mine: () => 'organization/role/mine/', - remove: (orgUuid: string, roleUuid: string) => - `organization/${orgUuid}/role/${roleUuid}/`, - join: (orgUuid: string, roleUuid: string) => - `organization/${orgUuid}/role/${roleUuid}/join/`, - }, + }, + + invites: { + list: (organizationUuid: string) => `invite/?organization_uuid=${organizationUuid}`, + create: (organizationUuid: string, maxUses: number) => + `invite/?organization_uuid=${organizationUuid}&max_uses=${maxUses}`, + revoke: (organizationUuid: string, inviteUuid: string) => + `invite/${inviteUuid}/?organization_uuid=${organizationUuid}`, + join: (inviteUuid: string) => `invite/join/?invite_uuid=${inviteUuid}`, + }, + + roles: { + list: (organizationUuid: string) => `role/?organization_uuid=${organizationUuid}`, + mine: () => 'role/mine/', + remove: (organizationUuid: string, roleUuid: string) => + `role/${roleUuid}/?organization_uuid=${organizationUuid}`, + join: (organizationUuid: string, roleUuid: string) => + `role/${roleUuid}/join/?organization_uuid=${organizationUuid}`, }, knowledge: { @@ -144,6 +146,7 @@ export const API = { list: () => 'onboarding-session/', byId: (uuid: string) => `onboarding-session/${uuid}/`, interact: (uuid: string) => `onboarding-session/${uuid}/interact/`, + askKa: (uuid: string) => `onboarding-session/${uuid}/ask-ka/`, history: (uuid: string) => `onboarding-session/${uuid}/history/`, complete: (uuid: string) => `onboarding-session/${uuid}/complete/`, }, diff --git a/site/src/views/InviteAccept.vue b/site/src/views/InviteAccept.vue index 8ad338f..4a4ecd3 100644 --- a/site/src/views/InviteAccept.vue +++ b/site/src/views/InviteAccept.vue @@ -19,7 +19,7 @@ const acceptInvite = async () => { error.value = null try { const response = await apiClient.post<{ message?: string; organization?: { uuid?: string } }>( - API.organization.invites.join(inviteUuid), + API.invites.join(inviteUuid), ) joinedOrganizationUuid.value = response.data?.organization?.uuid || null message.success(response.data?.message || 'Successfully joined organization') @@ -84,7 +84,7 @@ onMounted(() => { } .org-info { - background: #1f2937; + background: #f8fafc; border-radius: 8px; padding: 1.5rem; margin: 2rem 0; diff --git a/site/src/views/OrganizationManage.vue b/site/src/views/OrganizationManage.vue index b218bbe..050420d 100644 --- a/site/src/views/OrganizationManage.vue +++ b/site/src/views/OrganizationManage.vue @@ -103,7 +103,7 @@ const fetchMembers = async () => { const fetchInvites = async () => { try { - const response = await apiClient.get(API.organization.invites.list(organizationUuid)) + const response = await apiClient.get(API.invites.list(organizationUuid)) invites.value = response.data } catch (error) { console.error('Failed to fetch invites:', error) @@ -112,7 +112,7 @@ const fetchInvites = async () => { const fetchRoles = async () => { try { - const response = await apiClient.get(API.organization.roles.list(organizationUuid)) + const response = await apiClient.get(API.roles.list(organizationUuid)) Roles.value = response.data as unknown as Role[] } catch (error) { console.error('Failed to fetch Roles:', error) @@ -142,7 +142,7 @@ const createRole = async () => { creatingRole.value = true try { - await apiClient.post(API.organization.roles.list(organizationUuid), { name, description }) + await apiClient.post(API.roles.list(organizationUuid), { name, description }) message.success('Role created successfully') roleModalVisible.value = false resetRoleForm() @@ -169,7 +169,7 @@ const deleteRole = async (role: Role) => { onOk: async () => { deletingRoleUuid.value = role.uuid try { - await apiClient.delete(API.organization.roles.remove(organizationUuid, role.uuid)) + await apiClient.delete(API.roles.remove(organizationUuid, role.uuid)) message.success('Role deleted successfully') await fetchRoles() } catch (error) { @@ -189,7 +189,7 @@ const deleteRole = async (role: Role) => { const createInvite = async () => { try { const response = await apiClient.post( - API.organization.invites.create(organizationUuid, newInviteMaxUses.value), + API.invites.create(organizationUuid, newInviteMaxUses.value), ) newInviteUrl.value = response.data.invite_url inviteModalVisible.value = true @@ -200,19 +200,59 @@ const createInvite = async () => { } } -const copyInviteUrl = () => { - window.navigator.clipboard.writeText(newInviteUrl.value) - message.success('Invite URL copied to clipboard') +const fallbackCopyText = (text: string): boolean => { + const textarea = document.createElement('textarea') + textarea.value = text + textarea.setAttribute('readonly', 'true') + textarea.style.position = 'fixed' + textarea.style.opacity = '0' + textarea.style.pointerEvents = 'none' + document.body.appendChild(textarea) + textarea.focus() + textarea.select() + + const copied = document.execCommand('copy') + document.body.removeChild(textarea) + return copied } -const copyUrl = (url: string) => { - window.navigator.clipboard.writeText(url) - message.success('Copied to clipboard') +const copyToClipboard = async (text: string): Promise => { + const safeText = String(text || '').trim() + if (!safeText) return false + + if (window.isSecureContext && window.navigator.clipboard?.writeText) { + try { + await window.navigator.clipboard.writeText(safeText) + return true + } catch { + // Fall through to legacy copy for restricted browser contexts. + } + } + + return fallbackCopyText(safeText) +} + +const copyInviteUrl = async () => { + const copied = await copyToClipboard(newInviteUrl.value) + if (copied) { + message.success('Invite URL copied to clipboard') + return + } + message.error('Could not copy invite URL. Please copy it manually.') +} + +const copyUrl = async (url: string) => { + const copied = await copyToClipboard(url) + if (copied) { + message.success('Copied to clipboard') + return + } + message.error('Could not copy URL. Please copy it manually.') } const revokeInvite = async (inviteUuid: string) => { try { - await apiClient.delete(API.organization.invites.revoke(organizationUuid, inviteUuid)) + await apiClient.delete(API.invites.revoke(organizationUuid, inviteUuid)) message.success('Invite revoked') fetchInvites() } catch (error) { @@ -280,7 +320,7 @@ onMounted(async () => {
- + Description
@@ -308,7 +348,7 @@ onMounted(async () => {
- + Members ({{ filteredMembers.length }}) {
- + Invite Tokens @@ -412,7 +452,7 @@ onMounted(async () => {
- + Roles ({{ filteredRoles.length }}) @@ -549,30 +589,30 @@ onMounted(async () => { :deep(.ant-tabs-tab), :deep(.ant-input-number), :deep(.ant-input-number-input) { - color: #e5e7eb !important; + color: #1f2937 !important; } :deep(.ant-typography-secondary) { - color: #cbd5e1 !important; + color: #6b7280 !important; } :deep(.ant-input-number) { - background: #111827; - border-color: #334155; + background: #ffffff; + border-color: #d0d8e2; } :deep(.search-input) { - background: #1f2937 !important; - border-color: #475569 !important; - color: #f8fafc !important; + background: #ffffff !important; + border-color: #d0d8e2 !important; + color: #1f2937 !important; } :deep(.search-input::placeholder) { - color: #cbd5e1 !important; + color: #6b7280 !important; } :deep(.search-input::selection) { - background: #475569 !important; - color: #f8fafc !important; + background: #dbeafe !important; + color: #1f2937 !important; }