feat(subscription): allow admin to delete a granted subscription
Granting stacks a new row and extends the existing expiry, so a mistaken
grant had no way back: the report tab was read-only and no endpoint deleted
a subscription (only DELETE .../subscription/period/{uuid}, a different
resource).
Adds DELETE /api/v1/admin/subscription/{uuid}. Payment-backed subscriptions
are refused with 409 — deleting one would leave the sales report with a
payment that owns nothing; refunds are the correct path there.
The report tab gets a per-row delete with a confirm dialog, plus a button
that jumps to the grant tab so add and remove live in the same place.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import AdminSubscriptionPage from './AdminSubscriptionPage';
|
||||
const get = api.get as ReturnType<typeof vi.fn>;
|
||||
const patch = api.patch as ReturnType<typeof vi.fn>;
|
||||
const post = api.post as ReturnType<typeof vi.fn>;
|
||||
const del = api.delete as ReturnType<typeof vi.fn>;
|
||||
|
||||
const PLANS = [
|
||||
{
|
||||
@@ -213,7 +214,27 @@ describe('AdminSubscriptionPage — اعطای اشتراک', () => {
|
||||
});
|
||||
|
||||
describe('AdminSubscriptionPage — گزارش', () => {
|
||||
beforeEach(() => { get.mockReset(); post.mockReset(); });
|
||||
beforeEach(() => { get.mockReset(); post.mockReset(); del.mockReset(); });
|
||||
|
||||
/** یک ردیف گزارشِ اعطایی — تستهای حذف و ناوبری از همین استفاده میکنند. */
|
||||
function mockReportRow() {
|
||||
get.mockImplementation((url: string) => {
|
||||
if (url.includes('/admin/subscription/plans')) return Promise.resolve({ success: true, data: PLANS });
|
||||
if (url.includes('/admin/subscription/report')) {
|
||||
return Promise.resolve({
|
||||
success: true,
|
||||
meta: { totalRecords: 1 },
|
||||
data: [{
|
||||
uuid: 's-1', entityType: 'clinic', entityId: 11, entityName: 'کلینیک نمونه',
|
||||
isTrial: false, isGranted: true, grantedBy: 'ادمین',
|
||||
startsAt: 1700000000, expiresAt: 1800000000, createdAt: 1700000000,
|
||||
plan_name: 'professional', plan_level: 2,
|
||||
}],
|
||||
});
|
||||
}
|
||||
return Promise.resolve({ success: true, data: [], meta: { totalRecords: 0 } });
|
||||
});
|
||||
}
|
||||
|
||||
it('اشتراک اعطایی را «اعطایی» نشان میدهد، نه «پولی»', async () => {
|
||||
get.mockImplementation((url: string) => {
|
||||
@@ -241,4 +262,27 @@ describe('AdminSubscriptionPage — گزارش', () => {
|
||||
expect(screen.getByText('ادمین')).toBeInTheDocument();
|
||||
expect(screen.queryByText('پولی')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('حذف اشتراک پس از تأیید، DELETE میفرستد', async () => {
|
||||
mockReportRow();
|
||||
del.mockResolvedValue({ success: true, data: null });
|
||||
|
||||
renderWithProviders(<AdminSubscriptionPage />, { route: '/admin/admin-subscription' });
|
||||
fireEvent.click(await screen.findByText('گزارش فروش'));
|
||||
|
||||
fireEvent.click(await screen.findByLabelText('حذف اشتراک کلینیک نمونه'));
|
||||
fireEvent.click(await screen.findByRole('button', { name: 'حذف' }));
|
||||
|
||||
await waitFor(() => expect(del).toHaveBeenCalledWith('/api/v1/admin/subscription/s-1'));
|
||||
});
|
||||
|
||||
it('دکمهٔ اعطای اشتراک از گزارش به تب اعطا میبرد', async () => {
|
||||
mockReportRow();
|
||||
|
||||
renderWithProviders(<AdminSubscriptionPage />, { route: '/admin/admin-subscription' });
|
||||
fireEvent.click(await screen.findByText('گزارش فروش'));
|
||||
fireEvent.click(await screen.findByRole('button', { name: /اعطای اشتراک جدید/ }));
|
||||
|
||||
expect(await screen.findByText(/پلن و دوره/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user