chore: support show admin user page in demo website
This commit is contained in:
parent
50f44fb84c
commit
e284de79a8
@ -78,9 +78,10 @@ NEXT_PUBLIC_STRIPE_PRICE_LIFETIME=""
|
||||
# -----------------------------------------------------------------------------
|
||||
# Configurations
|
||||
# -----------------------------------------------------------------------------
|
||||
# Disable image optimization
|
||||
# -----------------------------------------------------------------------------
|
||||
# Disable image optimization, check out next.config.ts for more details
|
||||
DISABLE_IMAGE_OPTIMIZATION="false"
|
||||
# Run this website as demo website, in most cases, you should set this to false
|
||||
RUN_AS_DEMO_WEBSITE="false"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Analytics
|
||||
|
@ -441,6 +441,7 @@
|
||||
"title": "Admin",
|
||||
"users": {
|
||||
"title": "Users",
|
||||
"fakeData": "Note: Faked data for demonstration, some features are disabled",
|
||||
"error": "Failed to get users",
|
||||
"search": "Search users...",
|
||||
"columns": {
|
||||
|
@ -442,6 +442,7 @@
|
||||
"title": "系统管理",
|
||||
"users": {
|
||||
"title": "用户管理",
|
||||
"fakeData": "注:只为演示功能,数据为假数据,封禁功能不可用",
|
||||
"error": "获取用户失败",
|
||||
"search": "搜索用户...",
|
||||
"columns": {
|
||||
|
@ -51,6 +51,9 @@ export function UserDetailViewer({ user }: UserDetailViewerProps) {
|
||||
const [banExpiresAt, setBanExpiresAt] = useState<Date | undefined>();
|
||||
const triggerRefresh = useUsersStore((state) => state.triggerRefresh);
|
||||
|
||||
// show fake data in demo website
|
||||
const isDemo = process.env.NEXT_PUBLIC_DEMO_WEBSITE === 'true';
|
||||
|
||||
const handleBan = async () => {
|
||||
if (!banReason) {
|
||||
setError(t('ban.error'));
|
||||
@ -131,7 +134,7 @@ export function UserDetailViewer({ user }: UserDetailViewerProps) {
|
||||
className="size-8 border"
|
||||
/>
|
||||
<span className="hover:underline hover:underline-offset-4">
|
||||
{user.name}
|
||||
{isDemo ? 'MkSaaS User' : user.name}
|
||||
</span>
|
||||
</div>
|
||||
</Button>
|
||||
@ -145,8 +148,10 @@ export function UserDetailViewer({ user }: UserDetailViewerProps) {
|
||||
className="size-12 border"
|
||||
/>
|
||||
<div>
|
||||
<DrawerTitle>{user.name}</DrawerTitle>
|
||||
<DrawerDescription>{user.email}</DrawerDescription>
|
||||
<DrawerTitle>{isDemo ? 'MkSaaS User' : user.name}</DrawerTitle>
|
||||
<DrawerDescription>
|
||||
{isDemo ? 'example@mksaas.com' : user.email}
|
||||
</DrawerDescription>
|
||||
</div>
|
||||
</div>
|
||||
</DrawerHeader>
|
||||
@ -158,7 +163,7 @@ export function UserDetailViewer({ user }: UserDetailViewerProps) {
|
||||
variant={user.role === 'admin' ? 'default' : 'outline'}
|
||||
className="px-1.5"
|
||||
>
|
||||
{t(user.role === 'admin' ? 'admin' : 'user')}
|
||||
{user.role === 'admin' ? t('admin') : t('user')}
|
||||
</Badge>
|
||||
{/* email verified */}
|
||||
<Badge variant="outline" className="px-1.5 hover:bg-accent">
|
||||
@ -201,10 +206,10 @@ export function UserDetailViewer({ user }: UserDetailViewerProps) {
|
||||
{/* ban or unban user */}
|
||||
{user.banned ? (
|
||||
<div className="grid gap-4">
|
||||
<div className="text-muted-foreground">
|
||||
<div className="">
|
||||
{t('ban.reason')}: {user.banReason}
|
||||
</div>
|
||||
<div className="text-muted-foreground">
|
||||
<div className="">
|
||||
{t('ban.expires')}:{' '}
|
||||
{(user.banExpires && formatDate(user.banExpires)) ||
|
||||
t('ban.never')}
|
||||
@ -212,7 +217,7 @@ export function UserDetailViewer({ user }: UserDetailViewerProps) {
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={handleUnban}
|
||||
disabled={isLoading}
|
||||
disabled={isLoading || isDemo}
|
||||
className="mt-4 cursor-pointer"
|
||||
>
|
||||
{isLoading && (
|
||||
@ -271,7 +276,7 @@ export function UserDetailViewer({ user }: UserDetailViewerProps) {
|
||||
<Button
|
||||
type="submit"
|
||||
variant="destructive"
|
||||
disabled={isLoading || !banReason}
|
||||
disabled={isLoading || !banReason || isDemo}
|
||||
className="mt-4 cursor-pointer"
|
||||
>
|
||||
{isLoading && (
|
||||
|
@ -119,6 +119,9 @@ export function UsersTable({
|
||||
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
|
||||
|
||||
// show fake data in demo website
|
||||
const isDemo = process.env.NEXT_PUBLIC_DEMO_WEBSITE === 'true';
|
||||
|
||||
// Map column IDs to translation keys
|
||||
const columnIdToTranslationKey = {
|
||||
name: 'columns.name' as const,
|
||||
@ -165,7 +168,7 @@ export function UsersTable({
|
||||
) : (
|
||||
<MailQuestionIcon className="stroke-red-500 dark:stroke-red-400" />
|
||||
)}
|
||||
{user.email}
|
||||
{isDemo ? 'example@mksaas.com' : user.email}
|
||||
</Badge>
|
||||
</div>
|
||||
);
|
||||
@ -185,7 +188,7 @@ export function UsersTable({
|
||||
variant={role === 'admin' ? 'default' : 'outline'}
|
||||
className="px-1.5"
|
||||
>
|
||||
{t(role === 'admin' ? 'admin' : 'user')}
|
||||
{role === 'admin' ? t('admin') : t('user')}
|
||||
</Badge>
|
||||
</div>
|
||||
);
|
||||
@ -224,7 +227,7 @@ export function UsersTable({
|
||||
rel="noopener noreferrer"
|
||||
className="hover:underline hover:underline-offset-4"
|
||||
>
|
||||
{user.customerId}
|
||||
{!isDemo ? user.customerId : 'cus_abcdef123456'}
|
||||
</a>
|
||||
) : (
|
||||
'-'
|
||||
@ -323,15 +326,20 @@ export function UsersTable({
|
||||
return (
|
||||
<div className="w-full flex-col justify-start gap-6 space-y-4">
|
||||
<div className="flex items-center justify-between px-4 lg:px-6 gap-4">
|
||||
<Input
|
||||
placeholder={t('search')}
|
||||
value={search}
|
||||
onChange={(event) => {
|
||||
onSearch(event.target.value);
|
||||
onPageChange(0);
|
||||
}}
|
||||
className="max-w-sm"
|
||||
/>
|
||||
<div className="flex flex-1 items-center gap-4">
|
||||
<Input
|
||||
placeholder={t('search')}
|
||||
value={search}
|
||||
onChange={(event) => {
|
||||
onSearch(event.target.value);
|
||||
onPageChange(0);
|
||||
}}
|
||||
className="max-w-sm"
|
||||
/>
|
||||
{isDemo && (
|
||||
<span className="text-sm text-primary">{t('fakeData')}</span>
|
||||
)}
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="sm" className="cursor-pointer">
|
||||
|
@ -27,6 +27,9 @@ import { useTranslations } from 'next-intl';
|
||||
export function getSidebarLinks(): NestedMenuItem[] {
|
||||
const t = useTranslations('Dashboard');
|
||||
|
||||
// if is demo website, allow user to access admin and user pages, but data is fake
|
||||
const isDemo = process.env.NEXT_PUBLIC_DEMO_WEBSITE === 'true';
|
||||
|
||||
return [
|
||||
{
|
||||
title: t('dashboard.title'),
|
||||
@ -37,7 +40,7 @@ export function getSidebarLinks(): NestedMenuItem[] {
|
||||
{
|
||||
title: t('admin.title'),
|
||||
icon: <SettingsIcon className="size-4 shrink-0" />,
|
||||
authorizeOnly: ['admin'],
|
||||
authorizeOnly: isDemo ? ['admin', 'user'] : ['admin'],
|
||||
items: [
|
||||
{
|
||||
title: t('admin.users.title'),
|
||||
|
Loading…
Reference in New Issue
Block a user