0956e4facc
Modulo 5 da Fase 1 + quick wins fechados. features/tenantship/ com 2 services + 2 composables (members + invites). MembersPage.vue nova em views/pages/admin/ + rota /admin/members em routes.clinic. Migration 20260520000005 cria RPC accept_tenant_invite (SECURITY DEFINER + lock FOR UPDATE) — tenantInvitesRepository.acceptInvite agora chama RPC real (nao mais stub). SaasTenantFeaturesPage refatorada pra usar novo tenantFeatureAdminService. SetupWizardPage 2648 linhas deferido pra sessao dedicada. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
94 lines
3.1 KiB
JavaScript
94 lines
3.1 KiB
JavaScript
/*
|
|
|--------------------------------------------------------------------------
|
|
| Agência PSI
|
|
|--------------------------------------------------------------------------
|
|
| Arquivo: src/features/tenantship/composables/useTenantMembers.js
|
|
|
|
|
| Thin wrapper sobre tenantMembersRepository. Segue
|
|
| blueprints/composable-blueprint.md (Tipo A — thin wrapper default).
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
import { ref } from 'vue';
|
|
import { listForTenant, getById, updateMemberRole, updateMemberStatus, removeMember } from '@/features/tenantship/services/tenantMembersRepository';
|
|
|
|
export function useTenantMembers() {
|
|
const rows = ref([]);
|
|
const loading = ref(false);
|
|
const error = ref('');
|
|
|
|
async function loadForTenant({ tenantId, status } = {}) {
|
|
loading.value = true;
|
|
error.value = '';
|
|
try {
|
|
rows.value = await listForTenant({ tenantId, status });
|
|
} catch (e) {
|
|
error.value = e?.message || 'Falha ao carregar membros.';
|
|
rows.value = [];
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
async function fetchById(memberId, opts) {
|
|
loading.value = true;
|
|
error.value = '';
|
|
try {
|
|
return await getById(memberId, opts);
|
|
} catch (e) {
|
|
error.value = e?.message || 'Falha ao carregar membro.';
|
|
return null;
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
async function updateRole(memberId, role, opts) {
|
|
loading.value = true;
|
|
error.value = '';
|
|
try {
|
|
const updated = await updateMemberRole(memberId, role, opts);
|
|
const idx = rows.value.findIndex((r) => r.id === updated.id);
|
|
if (idx >= 0) rows.value[idx] = { ...rows.value[idx], role: updated.role };
|
|
return updated;
|
|
} catch (e) {
|
|
error.value = e?.message || 'Falha ao atualizar papel.';
|
|
throw e;
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
async function updateStatus(memberId, status, opts) {
|
|
loading.value = true;
|
|
error.value = '';
|
|
try {
|
|
const updated = await updateMemberStatus(memberId, status, opts);
|
|
const idx = rows.value.findIndex((r) => r.id === updated.id);
|
|
if (idx >= 0) rows.value[idx] = { ...rows.value[idx], status: updated.status };
|
|
return updated;
|
|
} catch (e) {
|
|
error.value = e?.message || 'Falha ao atualizar status.';
|
|
throw e;
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
async function remove(memberId, opts) {
|
|
loading.value = true;
|
|
error.value = '';
|
|
try {
|
|
await removeMember(memberId, opts);
|
|
rows.value = rows.value.filter((r) => r.id !== memberId);
|
|
return true;
|
|
} catch (e) {
|
|
error.value = e?.message || 'Falha ao remover membro.';
|
|
throw e;
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
return { rows, loading, error, loadForTenant, fetchById, updateRole, updateStatus, remove };
|
|
}
|