fix(crm): alta de usuarios carga compañías del tenant en vez de slug a mano
La organización destino salía como texto libre (o fallaba al listar todos los tenants del Hub con un token KC caduco). Ahora se carga desde /v1/auth/my-companies (mismo origen que el switcher) y se elige la compañía; el tenant_slug se resuelve de la compañía seleccionada. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,27 +1,24 @@
|
||||
import { fail } from '@sveltejs/kit';
|
||||
import type { PageServerLoad, Actions } from './$types';
|
||||
import { getKcAccessToken } from '$lib/server/api';
|
||||
import { listWorkspaceTenants, createWorkspaceInvite } from '$lib/server/workspace-provision';
|
||||
import { getUserCompanies } from '$lib/server/api';
|
||||
import { createWorkspaceInvite } from '$lib/server/workspace-provision';
|
||||
import { WORKSPACE_INVITE_ROLES } from '$lib/server/workspace-provision.shared';
|
||||
|
||||
export const load: PageServerLoad = async ({ cookies, fetch }) => {
|
||||
const accessToken = getKcAccessToken(cookies);
|
||||
if (!accessToken) {
|
||||
return { tenants: [], canListTenants: false, roles: WORKSPACE_INVITE_ROLES };
|
||||
}
|
||||
// La organización destino sale de las compañías del usuario (mismo origen que
|
||||
// el switcher, vía /v1/auth/my-companies). Así no se escribe el slug a mano ni
|
||||
// se depende de listar todos los tenants del Hub con un token KC que caduca.
|
||||
const companiesRaw = await getUserCompanies(cookies, fetch);
|
||||
const companies = (companiesRaw ?? [])
|
||||
.filter((c) => c && c.tenant_slug)
|
||||
.map((c) => ({
|
||||
id: c.id as number,
|
||||
name: c.name as string,
|
||||
tenant_slug: c.tenant_slug as string,
|
||||
tenant_name: (c.tenant_name as string) || (c.tenant_slug as string)
|
||||
}));
|
||||
|
||||
// Los hub_admin pueden listar todos los tenants (dropdown). Un admin de tenant
|
||||
// no puede listarlos (403) pero sí puede invitar a SU tenant escribiendo el slug.
|
||||
const res = await listWorkspaceTenants(accessToken, fetch);
|
||||
if (!res.ok) {
|
||||
return { tenants: [], canListTenants: false, roles: WORKSPACE_INVITE_ROLES };
|
||||
}
|
||||
|
||||
return {
|
||||
tenants: res.data.map((t) => ({ slug: t.slug, name: t.display_name || t.name })),
|
||||
canListTenants: true,
|
||||
roles: WORKSPACE_INVITE_ROLES
|
||||
};
|
||||
return { companies, roles: WORKSPACE_INVITE_ROLES };
|
||||
};
|
||||
|
||||
export const actions: Actions = {
|
||||
@@ -42,7 +39,7 @@ export const actions: Actions = {
|
||||
return fail(422, { error: 'Ingresa un email válido para invitar.', values });
|
||||
}
|
||||
if (!tenant_slug) {
|
||||
return fail(422, { error: 'Selecciona (o escribe) el slug de la organización destino.', values });
|
||||
return fail(422, { error: 'Selecciona la compañía destino.', values });
|
||||
}
|
||||
if (!WORKSPACE_INVITE_ROLES.includes(role as (typeof WORKSPACE_INVITE_ROLES)[number])) {
|
||||
return fail(422, { error: 'Rol inválido.', values });
|
||||
|
||||
@@ -75,13 +75,7 @@
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Nueva invitación</Card.Title>
|
||||
<Card.Description>
|
||||
{#if data.canListTenants}
|
||||
Elige la organización destino y el rol del usuario.
|
||||
{:else}
|
||||
Escribe el slug de tu organización y el rol del usuario.
|
||||
{/if}
|
||||
</Card.Description>
|
||||
<Card.Description>Elige la compañía destino y el rol del usuario.</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<form
|
||||
@@ -110,24 +104,19 @@
|
||||
</label>
|
||||
|
||||
<label class="flex flex-col gap-1 text-sm">
|
||||
<span class="font-medium">Organización *</span>
|
||||
{#if data.canListTenants}
|
||||
<span class="font-medium">Compañía destino *</span>
|
||||
{#if data.companies.length > 0}
|
||||
<select class={inputCls} name="tenant_slug" required value={prev('tenant_slug')}>
|
||||
<option value="" disabled>Selecciona…</option>
|
||||
{#each data.tenants as t (t.slug)}
|
||||
<option value={t.slug}>{t.name} ({t.slug})</option>
|
||||
{#each data.companies as c (c.id)}
|
||||
<option value={c.tenant_slug}>{c.name} — {c.tenant_name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else}
|
||||
<input
|
||||
class="font-mono {inputCls}"
|
||||
name="tenant_slug"
|
||||
required
|
||||
pattern="[a-z0-9-]+"
|
||||
placeholder="empresa-abc"
|
||||
title="Slug del tenant (minúsculas, dígitos y guiones)"
|
||||
value={prev('tenant_slug')}
|
||||
/>
|
||||
<span class="rounded-md border border-dashed px-3 py-2 text-xs text-muted-foreground">
|
||||
No hay compañías disponibles en tu tenant. Crea una en Compañías.
|
||||
</span>
|
||||
<input type="hidden" name="tenant_slug" value="" />
|
||||
{/if}
|
||||
</label>
|
||||
|
||||
@@ -142,7 +131,7 @@
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex justify-end border-t pt-4">
|
||||
<Button type="submit" disabled={submitting}>
|
||||
<Button type="submit" disabled={submitting || data.companies.length === 0}>
|
||||
{submitting ? 'Enviando…' : 'Enviar invitación'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user