Modified id usage to uuids, reset migrations, removed token from invites
This commit is contained in:
parent
529ab95a91
commit
c362c79912
18 changed files with 78 additions and 154 deletions
|
|
@ -37,11 +37,11 @@ class OrganizationAdmin(ModelAdmin):
|
|||
|
||||
@admin.register(Invite)
|
||||
class InviteAdmin(ModelAdmin):
|
||||
list_display = ('token', 'organization', 'created_by', 'is_active', 'uses', 'max_uses', 'expires_at')
|
||||
search_fields = ('token', 'organization__name', 'created_by__email_address')
|
||||
list_display = ('uuid', 'organization', 'created_by', 'is_active', 'uses', 'max_uses', 'expires_at')
|
||||
search_fields = ('uuid', 'organization__name', 'created_by__email_address')
|
||||
list_filter = ('is_active', 'expires_at')
|
||||
raw_id_fields = ('organization', 'created_by')
|
||||
readonly_fields = ('token', 'created_at')
|
||||
readonly_fields = ('uuid', 'created_at')
|
||||
|
||||
@admin.register(Role)
|
||||
class RoleAdmin(ModelAdmin):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
|
|
@ -61,17 +62,16 @@ class Migration(migrations.Migration):
|
|||
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='UUID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')),
|
||||
('token', models.UUIDField(default=uuid.uuid4, editable=False, unique=True, verbose_name='Token')),
|
||||
('expires_at', models.DateTimeField(verbose_name='Expires At')),
|
||||
('uses', models.IntegerField(default=0, verbose_name='Uses')),
|
||||
('max_uses', models.IntegerField(default=1, verbose_name='Max Uses')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='Is Active')),
|
||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='created_invites', to=settings.AUTH_USER_MODEL)),
|
||||
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invite_tokens', to='accounts.organization')),
|
||||
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invites', to='accounts.organization')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Invite Token',
|
||||
'verbose_name_plural': 'Invite Tokens',
|
||||
'verbose_name': 'Invite',
|
||||
'verbose_name_plural': 'Invites',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='invite',
|
||||
options={'verbose_name': 'Invite', 'verbose_name_plural': 'Invites'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='invite',
|
||||
name='organization',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invites', to='accounts.organization'),
|
||||
),
|
||||
]
|
||||
|
|
@ -65,7 +65,6 @@ class Organization(IdentifierMixin, TimeStampMixin, Model):
|
|||
|
||||
class Invite(IdentifierMixin, TimeStampMixin, Model):
|
||||
|
||||
token = UUIDField(verbose_name = _("Token"), default = uuid4, unique = True, editable = False)
|
||||
organization = ForeignKey(Organization, on_delete = CASCADE, related_name = "invites")
|
||||
created_by = ForeignKey(User, on_delete = CASCADE, related_name = "created_invites")
|
||||
expires_at = DateTimeField(verbose_name=_("Expires At"))
|
||||
|
|
|
|||
|
|
@ -32,14 +32,14 @@ class InviteSerializer(ModelSerializer):
|
|||
|
||||
class Meta:
|
||||
model = Invite
|
||||
fields = ['id', 'token', 'organization', 'created_by', 'expires_at', 'uses', 'max_uses', 'is_active', 'created_at', 'updated_at', 'invite_url', 'is_valid']
|
||||
read_only_fields = ['id', 'token', 'organization', 'created_by', 'created_at', 'updated_at']
|
||||
fields = ['id', 'uuid', 'organization', 'created_by', 'expires_at', 'uses', 'max_uses', 'is_active', 'created_at', 'updated_at', 'invite_url', 'is_valid']
|
||||
read_only_fields = ['id', 'uuid', 'organization', 'created_by', 'created_at', 'updated_at']
|
||||
|
||||
def get_invite_url(self, obj: Invite) -> str:
|
||||
request = self.context.get('request')
|
||||
if request:
|
||||
return request.build_absolute_uri(f'/invite/{obj.token}')
|
||||
return f'/invite/{obj.token}'
|
||||
return request.build_absolute_uri(f'/invite/{obj.uuid}')
|
||||
return f'/invite/{obj.uuid}'
|
||||
|
||||
def get_is_valid(self, obj: Invite) -> bool:
|
||||
return obj.is_valid()
|
||||
|
|
|
|||
|
|
@ -140,30 +140,30 @@ class OrganizationViewSet(ModelViewSet):
|
|||
)
|
||||
return Response(InviteSerializer(invitation, context={'request': request}).data)
|
||||
|
||||
@action(detail=True, methods=['delete'], url_path=r'revoke-invite/(?P<token>[0-9a-f-]{36})')
|
||||
def revoke_invite(self, request, uuid=None, token=None):
|
||||
@action(detail=True, methods=['delete'], url_path=r'revoke-invite/(?P<invite_uuid>[0-9a-f-]{36})')
|
||||
def revoke_invite(self, request, uuid=None, invite_uuid=None):
|
||||
organization = self.get_object()
|
||||
if not request.user.is_manager:
|
||||
return Response({'error': 'Only managers can revoke invites'}, status=HTTP_403_FORBIDDEN)
|
||||
invite = organization.invites.filter(token=token).first()
|
||||
invite = organization.invites.filter(uuid=invite_uuid).first()
|
||||
if not invite:
|
||||
return Response({'error': 'Invalid invitation token or not found in this organization'}, status=HTTP_404_NOT_FOUND)
|
||||
return Response({'error': 'Invalid invitation uuid or not found in this organization'}, status=HTTP_404_NOT_FOUND)
|
||||
invite.is_active = False
|
||||
invite.save()
|
||||
return Response({'message': 'Invitation successfully revoked'}, status=HTTP_200_OK)
|
||||
|
||||
@action(detail=False, methods=['post'], url_path='join/(?P<token>[0-9a-f-]{36})')
|
||||
def join(self, request, token=None):
|
||||
@action(detail=False, methods=['post'], url_path='join/(?P<invite_uuid>[0-9a-f-]{36})')
|
||||
def join(self, request, invite_uuid=None):
|
||||
try:
|
||||
invitation = Invite.objects.get(token=token)
|
||||
invitation = Invite.objects.get(uuid=invite_uuid)
|
||||
except Invite.DoesNotExist:
|
||||
return Response({'error': 'Not Found'}, status=HTTP_404_NOT_FOUND)
|
||||
|
||||
if not invitation.is_valid():
|
||||
return Response({'error': 'Invalid or expired token'}, status=HTTP_400_BAD_REQUEST)
|
||||
return Response({'error': 'Invalid or expired invitation'}, status=HTTP_400_BAD_REQUEST)
|
||||
|
||||
organization = invitation.organization
|
||||
if organization.members.filter(id=request.user.id).exists():
|
||||
if organization.members.filter(uuid=request.user.uuid).exists():
|
||||
return Response({'error': 'Already a member'}, status=HTTP_403_FORBIDDEN)
|
||||
|
||||
organization.members.add(request.user)
|
||||
|
|
@ -184,7 +184,7 @@ class OrganizationViewSet(ModelViewSet):
|
|||
if organization.owner == request.user:
|
||||
return Response({'error': 'Owner cannot leave'}, status=HTTP_403_FORBIDDEN)
|
||||
|
||||
if not organization.members.filter(id=request.user.id).exists():
|
||||
if not organization.members.filter(uuid=request.user.uuid).exists():
|
||||
return Response({'error': 'Not a member'}, status=HTTP_400_BAD_REQUEST)
|
||||
|
||||
organization.members.remove(request.user)
|
||||
|
|
@ -196,16 +196,16 @@ class OrganizationViewSet(ModelViewSet):
|
|||
serializer = UserSerializer(organization.members.all(), many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(detail=True, methods=['post'], url_path=r'member/(?P<user_id>\d+)/remove')
|
||||
def remove_member(self, request, uuid=None, user_id=None):
|
||||
@action(detail=True, methods=['post'], url_path=r'member/(?P<user_uuid>[0-9a-f-]{36})/remove')
|
||||
def remove_member(self, request, uuid=None, user_uuid=None):
|
||||
if not request.user.is_manager:
|
||||
return Response({'error': 'Forbidden'}, status=HTTP_403_FORBIDDEN)
|
||||
|
||||
organization = self.get_object()
|
||||
if str(organization.owner.id) == str(user_id):
|
||||
if str(organization.owner.uuid) == str(user_uuid):
|
||||
return Response({'error': 'Cannot remove owner'}, status=HTTP_403_FORBIDDEN)
|
||||
|
||||
user_to_remove = organization.members.filter(id=user_id).first()
|
||||
user_to_remove = organization.members.filter(uuid=user_uuid).first()
|
||||
if not user_to_remove:
|
||||
return Response({'error': 'Not found'}, status=HTTP_404_NOT_FOUND)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import django.db.models.deletion
|
||||
import pgvector.django
|
||||
import uuid
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
from pgvector.django import VectorExtension
|
||||
import django.db.models.deletion
|
||||
import pgvector.django.vector
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ class Migration(migrations.Migration):
|
|||
]
|
||||
|
||||
operations = [
|
||||
VectorExtension(),
|
||||
pgvector.django.VectorExtension(),
|
||||
migrations.CreateModel(
|
||||
name='TrainingFile',
|
||||
fields=[
|
||||
|
|
@ -48,7 +48,7 @@ class Migration(migrations.Migration):
|
|||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')),
|
||||
('content', models.TextField()),
|
||||
('content_hash', models.CharField(db_index=True, max_length=64)),
|
||||
('embedding', pgvector.django.VectorField(blank=True, dimensions=1536, null=True)),
|
||||
('embedding', pgvector.django.vector.VectorField(blank=True, dimensions=1536, null=True)),
|
||||
('metadata', models.JSONField(blank=True, default=dict)),
|
||||
('chunk_index', models.IntegerField(default=0)),
|
||||
('is_active', models.BooleanField(default=True)),
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ class Migration(migrations.Migration):
|
|||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')),
|
||||
('name', models.CharField(max_length=255, verbose_name='Agent Name')),
|
||||
('agent_type', models.CharField(choices=[('curriculum', 'Curriculum Agent (CA)'), ('knowledge', 'Knowledge Agent (KA)'), ('assessment', 'Assessment Agent (AA)'), ('monitor', 'Progress Monitor Agent (PMA)')], max_length=40, verbose_name='Agent Type')),
|
||||
('llm_config', models.JSONField(default=dict, verbose_name='LLM Configuration')),
|
||||
('system_prompt', models.TextField(verbose_name='System Prompt')),
|
||||
('tool_permissions', models.JSONField(default=list, verbose_name='Tool Permissions')),
|
||||
('llm_config', models.JSONField(blank=True, default=dict, null=True, verbose_name='LLM Configuration')),
|
||||
('system_prompt', models.TextField(blank=True, default='', verbose_name='System Prompt')),
|
||||
('tool_permissions', models.JSONField(blank=True, default=list, null=True, verbose_name='Tool Permissions')),
|
||||
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='agent_configs', to='accounts.organization', verbose_name='Organization')),
|
||||
],
|
||||
options={
|
||||
|
|
@ -40,6 +40,7 @@ class Migration(migrations.Migration):
|
|||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created At')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated At')),
|
||||
('title', models.CharField(max_length=255, verbose_name='Flow Title')),
|
||||
('structure', models.JSONField(blank=True, default=list, verbose_name='Flow Structure')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='Is Active')),
|
||||
('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='flows', to='accounts.role', verbose_name='Role')),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('onboarding', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='agentconfig',
|
||||
name='llm_config',
|
||||
field=models.JSONField(blank=True, default=dict, null=True, verbose_name='LLM Configuration'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='agentconfig',
|
||||
name='system_prompt',
|
||||
field=models.TextField(blank=True, default='', verbose_name='System Prompt'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='agentconfig',
|
||||
name='tool_permissions',
|
||||
field=models.JSONField(blank=True, default=list, null=True, verbose_name='Tool Permissions'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('onboarding', '0002_alter_agentconfig_llm_config_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='onboardingflow',
|
||||
name='structure',
|
||||
field=models.JSONField(blank=True, default=list, verbose_name='Flow Structure'),
|
||||
),
|
||||
]
|
||||
|
|
@ -94,8 +94,8 @@ export const API = {
|
|||
byId: (uuid: string) => `organization/${uuid}/`,
|
||||
members: {
|
||||
list: (uuid: string) => `organization/${uuid}/members/`,
|
||||
remove: (uuid: string, userId: number) =>
|
||||
`organization/${uuid}/member/${userId}/remove/`,
|
||||
remove: (uuid: string, userUuid: string) =>
|
||||
`organization/${uuid}/member/${userUuid}/remove/`,
|
||||
},
|
||||
invites: {
|
||||
list: (uuid: string) => `organization/${uuid}/invite/`,
|
||||
|
|
@ -103,7 +103,7 @@ export const API = {
|
|||
`organization/${uuid}/create-invite/?max_uses=${maxUses}`,
|
||||
revoke: (uuid: string, inviteUuid: string) =>
|
||||
`organization/${uuid}/revoke-invite/${inviteUuid}/`,
|
||||
join: (token: string) => `organization/join/${token}/`,
|
||||
join: (inviteUuid: string) => `organization/join/${inviteUuid}/`,
|
||||
},
|
||||
leave: (uuid: string) => `organization/${uuid}/leave/`,
|
||||
roles: {
|
||||
|
|
|
|||
|
|
@ -39,19 +39,19 @@ const router = createRouter({
|
|||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: '/organization/:id',
|
||||
path: '/organization/:organizationUuid',
|
||||
name: 'organization-detail',
|
||||
component: () => import('../views/OrganizationView.vue'),
|
||||
meta: { requiresAuth: true },
|
||||
},
|
||||
{
|
||||
path: '/organization/:id/manage',
|
||||
path: '/organization/:organizationUuid/manage',
|
||||
name: 'organization-manage',
|
||||
component: () => import('../views/OrganizationManage.vue'),
|
||||
meta: { requiresAuth: true, requiresManager: true },
|
||||
},
|
||||
{
|
||||
path: '/invite/:token',
|
||||
path: '/invite/:inviteUuid',
|
||||
name: 'invite-accept',
|
||||
component: () => import('../views/InviteAccept.vue'),
|
||||
},
|
||||
|
|
@ -62,7 +62,7 @@ const router = createRouter({
|
|||
meta: { requiresAuth: true, requiresManager: true },
|
||||
},
|
||||
{
|
||||
path: '/agents/:id',
|
||||
path: '/agents/:agentUuid',
|
||||
name: 'agent-detail',
|
||||
component: () => import('../views/AgentDetailView.vue'),
|
||||
meta: { requiresAuth: true, requiresManager: true },
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { User } from './user'
|
||||
|
||||
export interface Organization {
|
||||
id: number
|
||||
uuid: string
|
||||
name: string
|
||||
description: string
|
||||
|
|
@ -13,7 +12,6 @@ export interface Organization {
|
|||
}
|
||||
|
||||
export interface Role {
|
||||
id: number
|
||||
uuid: string
|
||||
name: string
|
||||
description?: string
|
||||
|
|
@ -24,8 +22,7 @@ export interface Role {
|
|||
}
|
||||
|
||||
export interface InviteToken {
|
||||
id: number
|
||||
token: string
|
||||
uuid: string
|
||||
invite_url: string
|
||||
created_by: User
|
||||
organization: Organization
|
||||
|
|
@ -36,7 +33,6 @@ export interface InviteToken {
|
|||
uses?: number
|
||||
}
|
||||
export interface TrainingFile {
|
||||
id: number
|
||||
uuid: string
|
||||
role: Role
|
||||
uploaded_by: User
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
export interface User {
|
||||
id: number
|
||||
uuid: string
|
||||
email_address: string
|
||||
first_name: string
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { apiClient, isAxiosError, API } from '../router/api'
|
|||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const token = route.params.token as string
|
||||
const inviteUuid = route.params.inviteUuid as string
|
||||
const loading = ref(false)
|
||||
const accepting = ref(false)
|
||||
const accepted = ref(false)
|
||||
|
|
@ -18,7 +18,7 @@ const acceptInvite = async () => {
|
|||
error.value = null
|
||||
try {
|
||||
const response = await apiClient.post<{ message: string; success: boolean; uuid: string }>(
|
||||
API.organization.invites.join(token),
|
||||
API.organization.invites.join(inviteUuid),
|
||||
)
|
||||
message.success(response.data?.message || 'Successfully joined organization')
|
||||
accepted.value = true
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ const route = useRoute()
|
|||
const router = useRouter()
|
||||
const auth = useUserStore()
|
||||
|
||||
const orgId = route.params.id as string
|
||||
const organizationUuid = route.params.organizationUuid as string
|
||||
const organization = ref<Organization | null>(null)
|
||||
const members = ref<User[]>([])
|
||||
const invites = ref<InviteToken[]>([])
|
||||
|
|
@ -48,7 +48,7 @@ const newDescription = ref('')
|
|||
const fetchOrganization = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await apiClient.get<Organization>(API.organization.byId(orgId))
|
||||
const response = await apiClient.get<Organization>(API.organization.byId(organizationUuid))
|
||||
organization.value = response.data
|
||||
newDescription.value = response.data.description
|
||||
} catch (error) {
|
||||
|
|
@ -61,7 +61,7 @@ const fetchOrganization = async () => {
|
|||
|
||||
const fetchMembers = async () => {
|
||||
try {
|
||||
const response = await apiClient.get<User[]>(API.organization.members.list(orgId))
|
||||
const response = await apiClient.get<User[]>(API.organization.members.list(organizationUuid))
|
||||
members.value = response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch members:', error)
|
||||
|
|
@ -70,7 +70,7 @@ const fetchMembers = async () => {
|
|||
|
||||
const fetchInvites = async () => {
|
||||
try {
|
||||
const response = await apiClient.get<InviteToken[]>(API.organization.invites.list(orgId))
|
||||
const response = await apiClient.get<InviteToken[]>(API.organization.invites.list(organizationUuid))
|
||||
invites.value = response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch invites:', error)
|
||||
|
|
@ -79,7 +79,7 @@ const fetchInvites = async () => {
|
|||
|
||||
const fetchRoles = async () => {
|
||||
try {
|
||||
const response = await apiClient.get<Role[]>(API.organization.roles.list(orgId))
|
||||
const response = await apiClient.get<Role[]>(API.organization.roles.list(organizationUuid))
|
||||
Roles.value = response.data as unknown as Role[]
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch Roles:', error)
|
||||
|
|
@ -109,7 +109,7 @@ const createRole = async () => {
|
|||
|
||||
creatingRole.value = true
|
||||
try {
|
||||
await apiClient.post(API.organization.roles.list(orgId), { name, description })
|
||||
await apiClient.post(API.organization.roles.list(organizationUuid), { name, description })
|
||||
message.success('Role created successfully')
|
||||
roleModalVisible.value = false
|
||||
resetRoleForm()
|
||||
|
|
@ -136,7 +136,7 @@ const deleteRole = async (role: Role) => {
|
|||
onOk: async () => {
|
||||
deletingRoleUuid.value = role.uuid
|
||||
try {
|
||||
await apiClient.delete(API.organization.roles.remove(orgId, role.uuid))
|
||||
await apiClient.delete(API.organization.roles.remove(organizationUuid, role.uuid))
|
||||
message.success('Role deleted successfully')
|
||||
await fetchRoles()
|
||||
} catch (error) {
|
||||
|
|
@ -156,7 +156,7 @@ const deleteRole = async (role: Role) => {
|
|||
const createInvite = async () => {
|
||||
try {
|
||||
const response = await apiClient.post<InviteToken>(
|
||||
API.organization.invites.create(orgId, newInviteMaxUses.value),
|
||||
API.organization.invites.create(organizationUuid, newInviteMaxUses.value),
|
||||
)
|
||||
newInviteUrl.value = response.data.invite_url
|
||||
inviteModalVisible.value = true
|
||||
|
|
@ -177,9 +177,9 @@ const copyUrl = (url: string) => {
|
|||
message.success('Copied to clipboard')
|
||||
}
|
||||
|
||||
const revokeInvite = async (token: string) => {
|
||||
const revokeInvite = async (inviteUuid: string) => {
|
||||
try {
|
||||
await apiClient.delete(API.organization.invites.revoke(orgId, token))
|
||||
await apiClient.delete(API.organization.invites.revoke(organizationUuid, inviteUuid))
|
||||
message.success('Invite revoked')
|
||||
fetchInvites()
|
||||
} catch (error) {
|
||||
|
|
@ -188,9 +188,9 @@ const revokeInvite = async (token: string) => {
|
|||
}
|
||||
}
|
||||
|
||||
const removeMember = async (userId: number) => {
|
||||
const removeMember = async (userUuid: string) => {
|
||||
try {
|
||||
await apiClient.post(API.organization.members.remove(orgId, userId))
|
||||
await apiClient.post(API.organization.members.remove(organizationUuid, userUuid))
|
||||
message.success('Member removed')
|
||||
fetchMembers()
|
||||
} catch (error) {
|
||||
|
|
@ -203,7 +203,7 @@ const removeMember = async (userId: number) => {
|
|||
|
||||
const saveDescription = async () => {
|
||||
try {
|
||||
await apiClient.patch(API.organization.byId(orgId), {
|
||||
await apiClient.patch(API.organization.byId(organizationUuid), {
|
||||
description: newDescription.value,
|
||||
})
|
||||
message.success('Description updated')
|
||||
|
|
@ -221,14 +221,14 @@ onMounted(async () => {
|
|||
await fetchInvites()
|
||||
await fetchRoles()
|
||||
|
||||
const currentUserId = auth.user?.id
|
||||
const isOwner = organization.value?.owner?.id === currentUserId
|
||||
const myMembership = members.value.find((m) => m.id === currentUserId)
|
||||
const currentUserUuid = auth.user?.uuid
|
||||
const isOwner = organization.value?.owner?.uuid === currentUserUuid
|
||||
const myMembership = members.value.find((member) => member.uuid === currentUserUuid)
|
||||
const isEmployer = myMembership?.is_manager
|
||||
|
||||
if (!isOwner && !isEmployer) {
|
||||
message.error('You do not have permission to manage this organization')
|
||||
router.replace(`/organization/${orgId}`)
|
||||
router.replace(`/organization/${organizationUuid}`)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
@ -239,7 +239,7 @@ onMounted(async () => {
|
|||
<Card v-if="organization" class="panel" :bordered="false">
|
||||
<div class="header">
|
||||
<Typography.Title :level="2">Manage {{ organization.name }}</Typography.Title>
|
||||
<Button type="default" @click="router.push(`/organization/${orgId}`)">
|
||||
<Button type="default" @click="router.push(`/organization/${organizationUuid}`)">
|
||||
Back to Organization
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -289,10 +289,10 @@ onMounted(async () => {
|
|||
/>
|
||||
<Space>
|
||||
<Button
|
||||
v-if="item.id !== organization.owner.id"
|
||||
v-if="item.uuid !== organization.owner.uuid"
|
||||
danger
|
||||
size="small"
|
||||
@click="removeMember(item.id)"
|
||||
@click="removeMember(item.uuid)"
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
|
|
@ -343,7 +343,7 @@ onMounted(async () => {
|
|||
<Button
|
||||
danger
|
||||
size="small"
|
||||
@click="revokeInvite(item.token)"
|
||||
@click="revokeInvite(item.uuid)"
|
||||
>
|
||||
Revoke
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@ import type { Role, Organization, TrainingFile } from '../types/organization'
|
|||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const orgId = route.params.id as string
|
||||
const organizationUuid = route.params.organizationUuid as string
|
||||
|
||||
const organization = ref<Organization | null>(null)
|
||||
const roles = ref<Role[]>([])
|
||||
const members = ref<Array<{ user: { id: number }; role: string }>>([])
|
||||
const members = ref<Array<{ uuid: string; is_manager?: boolean }>>([])
|
||||
const trainingFiles = ref<TrainingFile[]>([])
|
||||
const loading = ref(false)
|
||||
const uploading = ref(false)
|
||||
|
|
@ -38,14 +38,14 @@ const isManager = computed(() => {
|
|||
if (!auth.user || !organization.value) return false
|
||||
if ((organization.value as Organization & { is_manager?: boolean }).is_manager === true)
|
||||
return true
|
||||
if (organization.value.owner?.id === auth.user.id) return true
|
||||
return members.value.some((m) => m.user?.id === auth.user?.id && m.role === 'employer')
|
||||
if (organization.value.owner?.uuid === auth.user.uuid) return true
|
||||
return members.value.some((member) => member.uuid === auth.user?.uuid && member.is_manager)
|
||||
})
|
||||
|
||||
const fetchOrganization = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await apiClient.get<Organization>(API.organization.byId(orgId))
|
||||
const response = await apiClient.get<Organization>(API.organization.byId(organizationUuid))
|
||||
organization.value = response.data
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch organization:', error)
|
||||
|
|
@ -86,7 +86,7 @@ const isRoleJoined = (roleUuid: string | undefined) => {
|
|||
const fetchMembers = async () => {
|
||||
if (!organization.value?.uuid) return
|
||||
try {
|
||||
const response = await apiClient.get<Array<{ user: { id: number }; role: string }>>(
|
||||
const response = await apiClient.get<Array<{ uuid: string; is_manager?: boolean }>>(
|
||||
API.organization.members.list(organization.value.uuid),
|
||||
)
|
||||
members.value = response.data
|
||||
|
|
@ -100,7 +100,7 @@ const selectRole = async (roleUuid: string) => {
|
|||
message.error('Organization not loaded')
|
||||
return
|
||||
}
|
||||
if (!auth.user?.id) {
|
||||
if (!auth.user?.uuid) {
|
||||
try {
|
||||
await auth.fetchSession(true)
|
||||
} catch {
|
||||
|
|
@ -315,7 +315,7 @@ const trainingFileColumns = [
|
|||
title: 'Action',
|
||||
key: 'action',
|
||||
customRender: ({ record }: { record: TrainingFile }) => {
|
||||
if (isManager.value || auth.user?.id === record.uploaded_by?.id) {
|
||||
if (isManager.value || auth.user?.uuid === record.uploaded_by?.uuid) {
|
||||
return h(
|
||||
Button,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,8 +25,7 @@ const fetchOrganizations = async () => {
|
|||
|
||||
if (organizations.value.length === 1 && !auth.isGeneralManager) {
|
||||
const onlyOrg = organizations.value[0]
|
||||
const id = onlyOrg.uuid || String(onlyOrg.id)
|
||||
await router.replace(`/organization/${id}`)
|
||||
await router.replace(`/organization/${onlyOrg.uuid}`)
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
console.error('Failed to fetch organizations:', err)
|
||||
|
|
@ -52,8 +51,7 @@ onMounted(() => {
|
|||
})
|
||||
|
||||
const openOrg = (org: Organization) => {
|
||||
const id = org.uuid || String(org.id)
|
||||
router.push(`/organization/${id}`)
|
||||
router.push(`/organization/${org.uuid}`)
|
||||
}
|
||||
|
||||
const resetCreateOrganizationForm = () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue