Remove billing and checkout redirect buttons and related services
Deleted the billing-redirect-button, checkout-redirect-button, and embedded-stripe-checkout components. Additionally, removed the shadcn directory, which encompassed billing-related icons. This change streamlines the subscription settings interface and organizes the system's payment management. This update is a stepping stone towards improving the billing system's overall architecture.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { notFound } from 'next/navigation';
|
||||
|
||||
import isUserSuperAdmin from '~/admin/utils/is-user-super-admin';
|
||||
|
||||
import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-client';
|
||||
|
||||
import isUserSuperAdmin from '~/admin/utils/is-user-super-admin';
|
||||
|
||||
export function withAdminSession<Args extends unknown[], Response>(
|
||||
fn: (...params: Args) => Response,
|
||||
) {
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
import { revalidatePath } from 'next/cache';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
import { withAdminSession } from '~/admin/lib/actions-utils';
|
||||
|
||||
import { Logger } from '@kit/shared/logger';
|
||||
import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-client';
|
||||
|
||||
import { withAdminSession } from '~/admin/lib/actions-utils';
|
||||
|
||||
const getClient = () => getSupabaseServerActionClient({ admin: true });
|
||||
|
||||
export const deleteOrganizationAction = withAdminSession(
|
||||
|
||||
@@ -4,6 +4,8 @@ import { useState, useTransition } from 'react';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import type Organization from '@/lib/organizations/types/organization';
|
||||
|
||||
import useCsrfToken from '@kit/hooks/use-csrf-token';
|
||||
import { Button } from '@kit/ui/button';
|
||||
import {
|
||||
@@ -15,8 +17,6 @@ import {
|
||||
import { Input } from '@kit/ui/input';
|
||||
import { Label } from '@kit/ui/label';
|
||||
|
||||
import type Organization from '@/lib/organizations/types/organization';
|
||||
|
||||
import { deleteOrganizationAction } from '../actions.server';
|
||||
|
||||
function DeleteOrganizationModal({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import getSupabaseServerComponentClient from '@packages/supabase/server-component-client';
|
||||
|
||||
import { getOrganizationByUid } from '@/lib/organizations/database/queries';
|
||||
|
||||
import getSupabaseServerComponentClient from '@packages/supabase/server-component-client';
|
||||
|
||||
import AdminGuard from '../../../../../../packages/admin/components/AdminGuard';
|
||||
import DeleteOrganizationModal from '../components/DeleteOrganizationModal';
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
import Link from 'next/link';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
|
||||
import { DataTable } from '@/components/app/DataTable';
|
||||
import type Membership from '@/lib/organizations/types/membership';
|
||||
import type { ColumnDef } from '@tanstack/react-table';
|
||||
import { EllipsisVerticalIcon } from 'lucide-react';
|
||||
|
||||
@@ -15,10 +17,6 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from '@kit/ui/dropdown-menu';
|
||||
|
||||
import type Membership from '@/lib/organizations/types/membership';
|
||||
|
||||
import { DataTable } from '@/components/app/DataTable';
|
||||
|
||||
import RoleBadge from '../../../../../(app)/[account]/account/organization/components/RoleBadge';
|
||||
|
||||
type Data = {
|
||||
|
||||
@@ -2,15 +2,13 @@ import { use } from 'react';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import { PageBody } from '@/components/app/Page';
|
||||
import appConfig from '@/config/app.config';
|
||||
import { ChevronRightIcon } from 'lucide-react';
|
||||
|
||||
import AdminHeader from '@packages/admin/components/AdminHeader';
|
||||
import getSupabaseServerComponentClient from '@packages/supabase/server-component-client';
|
||||
|
||||
import appConfig from '@/config/app.config';
|
||||
|
||||
import { PageBody } from '@/components/app/Page';
|
||||
|
||||
import getPageFromQueryParams from '../../../utils/get-page-from-query-param';
|
||||
import { getMembershipsByOrganizationUid } from '../../queries';
|
||||
import OrganizationsMembersTable from './components/OrganizationsMembersTable';
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import type { ColumnDef } from '@tanstack/react-table';
|
||||
import { EllipsisIcon } from 'lucide-react';
|
||||
import { getI18n } from 'react-i18next';
|
||||
|
||||
import { Button } from '@kit/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuTrigger,
|
||||
} from '@kit/ui/dropdown-menu';
|
||||
|
||||
import pricingConfig from '@/config/pricing.config';
|
||||
|
||||
import { DataTable } from '@/components/app/DataTable';
|
||||
|
||||
import SubscriptionStatusBadge from '../../../(app)/[account]/components/organizations/SubscriptionStatusBadge';
|
||||
import type { getOrganizations } from '../queries';
|
||||
|
||||
type Response = Awaited<ReturnType<typeof getOrganizations>>;
|
||||
type Organizations = Response['organizations'];
|
||||
|
||||
const columns: ColumnDef<Organizations[0]>[] = [
|
||||
{
|
||||
header: 'ID',
|
||||
accessorKey: 'id',
|
||||
id: 'id',
|
||||
size: 10,
|
||||
},
|
||||
{
|
||||
header: 'UUID',
|
||||
accessorKey: 'uuid',
|
||||
id: 'uuid',
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
header: 'Name',
|
||||
accessorKey: 'name',
|
||||
id: 'name',
|
||||
},
|
||||
{
|
||||
header: 'Subscription',
|
||||
id: 'subscription',
|
||||
cell: ({ row }) => {
|
||||
const priceId = row.original?.subscription?.data?.priceId;
|
||||
|
||||
const plan = pricingConfig.products.find((product) => {
|
||||
return product.plans.some((plan) => plan.stripePriceId === priceId);
|
||||
});
|
||||
|
||||
if (plan) {
|
||||
const price = plan.plans.find((plan) => plan.stripePriceId === priceId);
|
||||
|
||||
if (!price) {
|
||||
return 'Unknown Price';
|
||||
}
|
||||
|
||||
return `${plan.name} - ${price.name}`;
|
||||
}
|
||||
|
||||
return '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Subscription Status',
|
||||
id: 'subscription-status',
|
||||
cell: ({ row }) => {
|
||||
const subscription = row.original?.subscription?.data;
|
||||
|
||||
if (!subscription) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return <SubscriptionStatusBadge subscription={subscription} />;
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Subscription Period',
|
||||
id: 'subscription-period',
|
||||
cell: ({ row }) => {
|
||||
const subscription = row.original?.subscription?.data;
|
||||
const i18n = getI18n();
|
||||
const language = i18n.language ?? 'en';
|
||||
|
||||
if (!subscription) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
const canceled = subscription.cancelAtPeriodEnd;
|
||||
const date = subscription.periodEndsAt;
|
||||
const formattedDate = new Date(date).toLocaleDateString(language);
|
||||
|
||||
return canceled ? (
|
||||
<span className={'text-orange-500'}>Stops on {formattedDate}</span>
|
||||
) : (
|
||||
<span className={'text-green-500'}>Renews on {formattedDate}</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Members',
|
||||
id: 'members',
|
||||
cell: ({ row }) => {
|
||||
const memberships = row.original.memberships.filter((item) => !item.code);
|
||||
const invites = row.original.memberships.length - memberships.length;
|
||||
const uid = row.original.uuid;
|
||||
const length = memberships.length;
|
||||
|
||||
return (
|
||||
<Link
|
||||
data-test={'organization-members-link'}
|
||||
href={`organizations/${uid}/members`}
|
||||
className={'cursor-pointer hover:underline'}
|
||||
>
|
||||
{length} member{length === 1 ? '' : 's'}{' '}
|
||||
{invites ? `(${invites} invites)` : ''}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: '',
|
||||
id: 'actions',
|
||||
cell: ({ row }) => {
|
||||
const organization = row.original;
|
||||
const uid = organization.uuid;
|
||||
|
||||
return (
|
||||
<div className={'flex justify-end'}>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button size={'icon'}>
|
||||
<span className="sr-only">Open menu</span>
|
||||
<EllipsisIcon className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigator.clipboard.writeText(uid)}
|
||||
>
|
||||
Copy UUID
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href={`/admin/organizations/${uid}/members`}>
|
||||
View Members
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
className={'text-red-500'}
|
||||
href={`/admin/organizations/${uid}/delete`}
|
||||
>
|
||||
Delete
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
function OrganizationsTable({
|
||||
organizations,
|
||||
pageCount,
|
||||
perPage,
|
||||
page,
|
||||
}: React.PropsWithChildren<{
|
||||
organizations: Organizations;
|
||||
pageCount: number;
|
||||
perPage: number;
|
||||
page: number;
|
||||
}>) {
|
||||
return (
|
||||
<DataTable
|
||||
tableProps={{
|
||||
'data-test': 'admin-organizations-table',
|
||||
}}
|
||||
pageSize={perPage}
|
||||
pageIndex={page - 1}
|
||||
pageCount={pageCount}
|
||||
columns={columns}
|
||||
data={organizations}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default OrganizationsTable;
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
||||
|
||||
import { PageBody } from '@/components/app/Page';
|
||||
|
||||
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
||||
|
||||
function OrganizationsAdminPageError() {
|
||||
return (
|
||||
<PageBody>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { PageBody } from '@/components/app/Page';
|
||||
import appConfig from '@/config/app.config';
|
||||
import AdminGuard from '@/packages/admin/components/AdminGuard';
|
||||
import AdminHeader from '@/packages/admin/components/AdminHeader';
|
||||
|
||||
@@ -5,10 +7,6 @@ import getSupabaseServerComponentClient from '@packages/supabase/server-componen
|
||||
|
||||
import { Input } from '@kit/ui/input';
|
||||
|
||||
import appConfig from '@/config/app.config';
|
||||
|
||||
import { PageBody } from '@/components/app/Page';
|
||||
|
||||
import OrganizationsTable from './components/OrganizationsTable';
|
||||
import { getOrganizations } from './queries';
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { SupabaseClient } from '@supabase/supabase-js';
|
||||
|
||||
import type { Database } from '@/database.types';
|
||||
|
||||
import { MEMBERSHIPS_TABLE, ORGANIZATIONS_TABLE } from '@/lib/db-tables';
|
||||
import type { UserOrganizationData } from '@/lib/organizations/database/queries';
|
||||
import type MembershipRole from '@/lib/organizations/types/membership-role';
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import getSupabaseServerComponentClient from '@packages/supabase/server-component-client';
|
||||
|
||||
import { PageBody } from '@/components/app/Page';
|
||||
import appConfig from '@/config/app.config';
|
||||
|
||||
import { PageBody } from '@/components/app/Page';
|
||||
import getSupabaseServerComponentClient from '@packages/supabase/server-component-client';
|
||||
|
||||
import AdminDashboard from '../../packages/admin/components/AdminDashboard';
|
||||
import AdminGuard from '../../packages/admin/components/AdminGuard';
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
import { revalidatePath } from 'next/cache';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
import { withAdminSession } from '~/admin/lib/actions-utils';
|
||||
|
||||
import { Logger } from '@kit/shared/logger';
|
||||
import { getSupabaseServerActionClient } from '@kit/supabase/server-actions-client';
|
||||
|
||||
import { withAdminSession } from '~/admin/lib/actions-utils';
|
||||
|
||||
const getClient = () => getSupabaseServerActionClient({ admin: true });
|
||||
|
||||
export const banUser = withAdminSession(async ({ userId }) => {
|
||||
|
||||
@@ -8,6 +8,8 @@ import { useRouter } from 'next/navigation';
|
||||
|
||||
import type { User } from '@supabase/gotrue-js';
|
||||
|
||||
import ErrorBoundary from '@/components/app/ErrorBoundary';
|
||||
|
||||
import useCsrfToken from '@kit/hooks/use-csrf-token';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
||||
import { Button } from '@kit/ui/button';
|
||||
@@ -20,8 +22,6 @@ import {
|
||||
import { Input } from '@kit/ui/input';
|
||||
import { Label } from '@kit/ui/label';
|
||||
|
||||
import ErrorBoundary from '@/components/app/ErrorBoundary';
|
||||
|
||||
import { banUser } from '../actions.server';
|
||||
|
||||
function BanUserModal({
|
||||
|
||||
@@ -4,10 +4,10 @@ import { useEffect } from 'react';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import useSupabase from '@kit/hooks/use-supabase';
|
||||
|
||||
import Spinner from '@/components/app/Spinner';
|
||||
|
||||
import useSupabase from '@kit/hooks/use-supabase';
|
||||
|
||||
function ImpersonateUserAuthSetter({
|
||||
tokens,
|
||||
}: React.PropsWithChildren<{
|
||||
|
||||
@@ -6,6 +6,9 @@ import { useRouter } from 'next/navigation';
|
||||
|
||||
import type { User } from '@supabase/gotrue-js';
|
||||
|
||||
import If from '@/components/app/If';
|
||||
import LoadingOverlay from '@/components/app/LoadingOverlay';
|
||||
|
||||
import useCsrfToken from '@kit/hooks/use-csrf-token';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
||||
import { Button } from '@kit/ui/button';
|
||||
@@ -16,9 +19,6 @@ import {
|
||||
DialogTitle,
|
||||
} from '@kit/ui/dialog';
|
||||
|
||||
import If from '@/components/app/If';
|
||||
import LoadingOverlay from '@/components/app/LoadingOverlay';
|
||||
|
||||
import { impersonateUser } from '../actions.server';
|
||||
import ImpersonateUserAuthSetter from '../components/ImpersonateUserAuthSetter';
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import If from '@/components/app/If';
|
||||
import { EllipsisVerticalIcon } from 'lucide-react';
|
||||
|
||||
import { Button } from '@kit/ui/button';
|
||||
@@ -12,8 +13,6 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from '@kit/ui/dropdown-menu';
|
||||
|
||||
import If from '@/components/app/If';
|
||||
|
||||
function UserActionsDropdown({
|
||||
uid,
|
||||
isBanned,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
import { PageBody } from '@/components/app/Page';
|
||||
import configuration from '@/config/app.config';
|
||||
import type MembershipRole from '@/lib/organizations/types/membership-role';
|
||||
import { ChevronRightIcon } from 'lucide-react';
|
||||
|
||||
import getSupabaseServerComponentClient from '@packages/supabase/server-component-client';
|
||||
@@ -17,12 +20,6 @@ import {
|
||||
TableRow,
|
||||
} from '@kit/ui/table';
|
||||
|
||||
import configuration from '@/config/app.config';
|
||||
|
||||
import type MembershipRole from '@/lib/organizations/types/membership-role';
|
||||
|
||||
import { PageBody } from '@/components/app/Page';
|
||||
|
||||
import RoleBadge from '../../../(app)/[account]/account/organization/components/RoleBadge';
|
||||
import AdminGuard from '../../../../packages/admin/components/AdminGuard';
|
||||
import AdminHeader from '../../../../packages/admin/components/AdminHeader';
|
||||
|
||||
@@ -1,239 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import type { ColumnDef } from '@tanstack/react-table';
|
||||
import { EllipsisIcon } from 'lucide-react';
|
||||
import { getI18n } from 'react-i18next';
|
||||
|
||||
import type UserData from '@kit/session/types/user-data';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@kit/ui/avatar';
|
||||
import { Badge } from '@kit/ui/badge';
|
||||
import { Button } from '@kit/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuTrigger,
|
||||
} from '@kit/ui/dropdown-menu';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@kit/ui/tooltip';
|
||||
|
||||
import { DataTable } from '@/components/app/DataTable';
|
||||
import If from '@/components/app/If';
|
||||
|
||||
type UserRow = {
|
||||
id: string;
|
||||
email: string | undefined;
|
||||
phone: string | undefined;
|
||||
createdAt: string;
|
||||
updatedAt: string | undefined;
|
||||
lastSignInAt: string | undefined;
|
||||
banDuration: string | undefined;
|
||||
data: UserData;
|
||||
};
|
||||
|
||||
const columns: ColumnDef<UserRow>[] = [
|
||||
{
|
||||
header: '',
|
||||
id: 'avatar',
|
||||
size: 10,
|
||||
cell: ({ row }) => {
|
||||
const user = row.original;
|
||||
const data = user.data;
|
||||
const displayName = data?.displayName;
|
||||
const photoUrl = data?.photoUrl;
|
||||
const displayText = displayName ?? user.email ?? user.phone ?? '';
|
||||
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Avatar>
|
||||
{photoUrl ? <AvatarImage src={photoUrl} /> : null}
|
||||
<AvatarFallback>{displayText[0]}</AvatarFallback>
|
||||
</Avatar>
|
||||
</TooltipTrigger>
|
||||
|
||||
<TooltipContent>{displayText}</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'ID',
|
||||
id: 'id',
|
||||
size: 30,
|
||||
cell: ({ row }) => {
|
||||
const id = row.original.id;
|
||||
|
||||
return (
|
||||
<Link className={'hover:underline'} href={`/admin/users/${id}`}>
|
||||
{id}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Email',
|
||||
id: 'email',
|
||||
cell: ({ row }) => {
|
||||
const email = row.original.email;
|
||||
|
||||
return (
|
||||
<span title={email} className={'block max-w-full truncate'}>
|
||||
{email}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Name',
|
||||
size: 50,
|
||||
id: 'displayName',
|
||||
cell: ({ row }) => {
|
||||
return row.original.data?.displayName ?? '';
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Created at',
|
||||
id: 'createdAt',
|
||||
cell: ({ row }) => {
|
||||
const date = new Date(row.original.createdAt);
|
||||
const i18n = getI18n();
|
||||
const language = i18n.language ?? 'en';
|
||||
const createdAtLabel = date.toLocaleDateString(language);
|
||||
|
||||
return <span>{createdAtLabel}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Last sign in',
|
||||
id: 'lastSignInAt',
|
||||
cell: ({ row }) => {
|
||||
const lastSignInAt = row.original.lastSignInAt;
|
||||
|
||||
if (!lastSignInAt) {
|
||||
return <span>-</span>;
|
||||
}
|
||||
|
||||
const date = new Date(lastSignInAt);
|
||||
return <span suppressHydrationWarning>{date.toLocaleString()}</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Status',
|
||||
id: 'status',
|
||||
cell: ({ row }) => {
|
||||
const banDuration = row.original.banDuration;
|
||||
|
||||
if (!banDuration || banDuration === 'none') {
|
||||
return (
|
||||
<Badge className={'inline-flex'} color={'success'}>
|
||||
Active
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge className={'inline-flex'} color={'error'}>
|
||||
Banned
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: '',
|
||||
id: 'actions',
|
||||
cell: ({ row }) => {
|
||||
const user = row.original;
|
||||
const banDuration = row.original.banDuration;
|
||||
const isBanned = banDuration && banDuration !== 'none';
|
||||
|
||||
return (
|
||||
<div className={'flex justify-end'}>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button size={'icon'}>
|
||||
<span className="sr-only">Open menu</span>
|
||||
<EllipsisIcon className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigator.clipboard.writeText(user.id)}
|
||||
>
|
||||
Copy user ID
|
||||
</DropdownMenuItem>
|
||||
|
||||
<If condition={!isBanned}>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href={`/admin/users/${user.id}/impersonate`}>
|
||||
Impersonate User
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
className={
|
||||
'text-orange-500 hover:bg-orange-50 dark:hover:bg-orange-500/5'
|
||||
}
|
||||
href={`/admin/users/${user.id}/ban`}
|
||||
>
|
||||
Ban User
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem asChild>
|
||||
<Link
|
||||
className={
|
||||
'text-red-500 hover:bg-red-50 dark:hover:bg-red-500/5'
|
||||
}
|
||||
href={`/admin/users/${user.id}/delete`}
|
||||
>
|
||||
Delete User
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</If>
|
||||
|
||||
<If condition={isBanned}>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href={`/admin/users/${user.id}/reactivate`}>
|
||||
Reactivate User
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
</If>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
function UsersTable({
|
||||
users,
|
||||
page,
|
||||
pageCount,
|
||||
perPage,
|
||||
}: React.PropsWithChildren<{
|
||||
users: UserRow[];
|
||||
pageCount: number;
|
||||
page: number;
|
||||
perPage: number;
|
||||
}>) {
|
||||
return (
|
||||
<DataTable
|
||||
tableProps={{
|
||||
'data-test': 'admin-users-table',
|
||||
}}
|
||||
pageIndex={page - 1}
|
||||
pageSize={perPage}
|
||||
pageCount={pageCount}
|
||||
data={users}
|
||||
columns={columns}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default UsersTable;
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
||||
|
||||
import { PageBody } from '@/components/app/Page';
|
||||
|
||||
import { Alert, AlertDescription, AlertTitle } from '@kit/ui/alert';
|
||||
|
||||
function UsersAdminPageError() {
|
||||
return (
|
||||
<PageBody>
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { PageBody } from '@/components/app/Page';
|
||||
import appConfig from '@/config/app.config';
|
||||
|
||||
import getSupabaseServerComponentClient from '@packages/supabase/server-component-client';
|
||||
|
||||
import type UserData from '@kit/session/types/user-data';
|
||||
|
||||
import appConfig from '@/config/app.config';
|
||||
|
||||
import { PageBody } from '@/components/app/Page';
|
||||
|
||||
import AdminGuard from '../../../packages/admin/components/AdminGuard';
|
||||
import AdminHeader from '../../../packages/admin/components/AdminHeader';
|
||||
import getPageFromQueryParams from '../utils/get-page-from-query-param';
|
||||
import UsersTable from './components/UsersTable';
|
||||
import { getUsers } from './queries';
|
||||
|
||||
interface UsersAdminPageProps {
|
||||
@@ -32,14 +30,7 @@ async function UsersAdminPage({ searchParams }: UsersAdminPageProps) {
|
||||
<div className={'flex flex-1 flex-col'}>
|
||||
<AdminHeader>Users</AdminHeader>
|
||||
|
||||
<PageBody>
|
||||
<UsersTable
|
||||
users={users}
|
||||
page={page}
|
||||
pageCount={pageCount}
|
||||
perPage={perPage}
|
||||
/>
|
||||
</PageBody>
|
||||
<PageBody></PageBody>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import getSupabaseServerComponentClient from '@packages/supabase/server-component-client';
|
||||
|
||||
import { USERS_TABLE } from '@/lib/db-tables';
|
||||
|
||||
import getSupabaseServerComponentClient from '@packages/supabase/server-component-client';
|
||||
|
||||
export async function getUsers(ids: string[]) {
|
||||
const client = getSupabaseServerComponentClient({ admin: true });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user