Skip to content
Merged
6 changes: 3 additions & 3 deletions src/modules/app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
<authPendingRequestBanner />

<organizationsAdminPendingBanner />
<organizationsSuggestedJoinBanner />
<organizationsLoginNotices />
Comment thread
coderabbitai[bot] marked this conversation as resolved.
<legalCookieBanner />
<legalFooterSection />
<v-main class="pb-0" :style="mainStyle">
Expand Down Expand Up @@ -64,7 +64,7 @@ import authEmailBanner from '../auth/components/emailBanner.component.vue';
import authPendingRequestBanner from '../auth/components/pendingRequestBanner.component.vue';

import organizationsAdminPendingBanner from '../organizations/components/organizations.adminPendingBanner.component.vue';
import organizationsSuggestedJoinBanner from '../organizations/components/organizations.suggestedJoinBanner.component.vue';
import organizationsLoginNotices from '../organizations/components/organizations.loginNotices.component.vue';
import legalCookieBanner from '../legal/components/legal.cookieBanner.component.vue';
import legalFooterSection from '../legal/components/legal.footerSection.component.vue';
import appErrorBoundary from './components/app.errorBoundary.component.vue';
Expand All @@ -82,7 +82,7 @@ export default {
authPendingRequestBanner,

organizationsAdminPendingBanner,
organizationsSuggestedJoinBanner,
organizationsLoginNotices,
legalCookieBanner,
legalFooterSection,
appErrorBoundary,
Expand Down
7 changes: 6 additions & 1 deletion src/modules/core/components/core.datatable.component.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
<template>
<v-card width="100%" class="datatable" color="surface" :flat="config.vuetify.theme.flat" :class="config.vuetify.theme.rounded">
<v-card-title class="d-flex align-center">
<v-card-title class="d-flex align-center ga-3">
<span class="text-title-medium">{{ title }}</span>
<v-spacer></v-spacer>
<!-- Optional card-level actions (e.g. an "Add" button) rendered inside the card
title, left of the search field. Named `toolbar` — NOT `actions` — because
row slots resolve dynamically via `header.slotName || header.value` and
several consumers already pass #actions for the row actions column. -->
<slot name="toolbar"></slot>
<v-text-field
v-if="search"
v-model="textSearch"
Expand Down
33 changes: 33 additions & 0 deletions src/modules/core/tests/core.datatable.component.unit.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,37 @@ describe('core.datatable.component', () => {
});
}).not.toThrow();
});

// Card-level #toolbar slot — renders inside the v-card-title, left of the search
// field. Named `toolbar` (not `actions`) to avoid colliding with the dynamic
// per-row slots resolved via `header.slotName || header.value`.
it('renders the #toolbar slot content in the card title', () => {
const wrapper = mount(CoreDatatable, {
props: { headers: [{ text: 'Name', value: 'name' }], items: [], fetchAction: vi.fn().mockResolvedValue() },
slots: { toolbar: '<button data-test="toolbar-action">Add</button>' },
global: globalOpts(vuetify),
});
const action = wrapper.find('[data-test="toolbar-action"]');
expect(action.exists()).toBe(true);
expect(action.element.closest('.v-card-title')).not.toBeNull();
});

it('does not collide with a per-row #actions slot (toolbar stays out of the rows)', () => {
const headers = [
{ text: 'Name', value: 'name' },
{ text: 'Actions', value: 'actions', kind: 'slot', slotName: 'actions' },
];
const items = [{ _id: '1', name: 'John' }];
const wrapper = mount(CoreDatatable, {
props: { headers, items, fetchAction: vi.fn().mockResolvedValue() },
slots: {
toolbar: '<button data-test="toolbar-action">Add</button>',
actions: '<span data-test="row-action">edit</span>',
},
global: globalOpts(vuetify),
});
// Row slot renders in the table body, toolbar slot in the card title — both present, independent.
expect(wrapper.find('[data-test="row-action"]').element.closest('tbody')).not.toBeNull();
expect(wrapper.find('[data-test="toolbar-action"]').element.closest('.v-card-title')).not.toBeNull();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ vi.mock('../../../lib/services/config', () => ({
}));

const stubs = {
// Render named slots so the #status/#invitedBy/#actions template functions are invoked
// Render named slots so the #toolbar/#status/#invitedBy/#actions template functions are invoked
coreDataTableComponent: {
template: `<div>
<slot name="toolbar" />
<slot name="status" :item="{ id:'s1', email:'a@b.co', usedAt: null, expiresAt: '2999-01-01', invitedBy: null }" />
<slot name="invitedBy" :item="{ id:'s2', email:'b@b.co', usedAt: null, expiresAt: null, invitedBy: { email: 'admin@b.co' } }" />
<slot name="invitedBy" :item="{ id:'s4', email:'d@b.co', usedAt: null, expiresAt: null, invitedBy: null }" />
Expand Down
20 changes: 14 additions & 6 deletions src/modules/invitations/views/invitations.admin.view.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,22 @@
</v-alert>
</v-col>

<v-col cols="12" class="d-flex justify-end">
<v-btn color="primary" variant="flat" :class="config.vuetify.theme.rounded" class="text-none" @click="openCreate">
<v-icon start icon="fa-solid fa-envelope"></v-icon>
Invite
</v-btn>
</v-col>
<v-col cols="12">
<coreDataTableComponent :headers="inviteHeaders" :items="invitations" :fetch-action="fetchInvitations" :search="false">
<!-- Card-level action: the Invite button lives inside the datatable card
title (top-right — no search on this table), like other admin tabs. -->
<template #toolbar>
<v-btn
color="primary"
variant="tonal"
:class="config.vuetify.theme.rounded"
class="text-none text-body-medium"
@click="openCreate"
>
<v-icon icon="fa-solid fa-envelope" size="small" class="mr-2"></v-icon>
Invite
</v-btn>
</template>
<template #status="{ item }">
<v-chip :color="inviteStatus(item).color" size="small" variant="tonal">{{ inviteStatus(item).label }}</v-chip>
</template>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
color="surface"
:flat="config.vuetify.theme.flat"
:class="config.vuetify.theme.rounded"
class="pa-6 mt-4"
class="pa-6 mt-3"
>
<h3 class="text-title-medium font-weight-medium mb-4">Roles & Permissions</h3>
<v-list density="compact" class="bg-transparent">
Expand All @@ -80,7 +80,7 @@
color="surface"
:flat="config.vuetify.theme.flat"
:class="config.vuetify.theme.rounded"
class="pa-6 mt-4"
class="pa-6 mt-3"
>
<h3 class="text-title-medium font-weight-medium mb-4">
Pending Join Requests
Expand Down
Loading
Loading