完成了一些接口
This commit is contained in:
@@ -20,6 +20,12 @@ import { Skeleton } from "@/components/ui/skeleton";
|
|||||||
import { updateUser } from "@/lib/api/admin/user";
|
import { updateUser } from "@/lib/api/admin/user";
|
||||||
import { AdminApi } from "@/lib/api";
|
import { AdminApi } from "@/lib/api";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
import {
|
||||||
|
Alert,
|
||||||
|
AlertDescription,
|
||||||
|
AlertTitle,
|
||||||
|
} from "@/components/ui/alert"
|
||||||
|
import { AlertCircle } from "lucide-react";
|
||||||
|
|
||||||
export function UserInfoEditor({
|
export function UserInfoEditor({
|
||||||
onClose,
|
onClose,
|
||||||
@@ -46,6 +52,14 @@ export function UserInfoEditor({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleRemove = async () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSetPassword = async () => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer open={!!userId} onClose={onClose} >
|
<Drawer open={!!userId} onClose={onClose} >
|
||||||
<DrawerContent>
|
<DrawerContent>
|
||||||
@@ -54,16 +68,20 @@ export function UserInfoEditor({
|
|||||||
<DrawerDescription>确保你在保存之前检查所有更改</DrawerDescription>
|
<DrawerDescription>确保你在保存之前检查所有更改</DrawerDescription>
|
||||||
</DrawerHeader>
|
</DrawerHeader>
|
||||||
|
|
||||||
{user && <ProfileForm className="px-4" user={user} onSubmit={(e) => {
|
{user && <ProfileForm className="px-4"
|
||||||
e.preventDefault()
|
user={user}
|
||||||
const formData = new FormData(e.currentTarget);
|
onSetPassword={handleSetPassword}
|
||||||
handleSave({
|
onRemove={handleRemove}
|
||||||
username: formData.get("username")?.toString()!,
|
onSubmit={(e) => {
|
||||||
nickname: formData.get("nickname")?.toString()!,
|
e.preventDefault()
|
||||||
email: formData.get("email")?.toString() || null,
|
const formData = new FormData(e.currentTarget);
|
||||||
phone: formData.get("phone")?.toString() || null,
|
handleSave({
|
||||||
})
|
username: formData.get("username")?.toString()!,
|
||||||
}} />}
|
nickname: formData.get("nickname")?.toString()!,
|
||||||
|
email: formData.get("email")?.toString() || null,
|
||||||
|
phone: formData.get("phone")?.toString() || null,
|
||||||
|
})
|
||||||
|
}} />}
|
||||||
|
|
||||||
{isLoading &&
|
{isLoading &&
|
||||||
[...Array(5)].map((_, i) => (
|
[...Array(5)].map((_, i) => (
|
||||||
@@ -71,6 +89,16 @@ export function UserInfoEditor({
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
error && (
|
||||||
|
<Alert variant="destructive" className="mx-4">
|
||||||
|
<AlertCircle className="h-6 w-6" />
|
||||||
|
<AlertTitle>出错啦!</AlertTitle>
|
||||||
|
<AlertDescription>{error.message}</AlertDescription>
|
||||||
|
</Alert>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
<DrawerFooter className="pt-2">
|
<DrawerFooter className="pt-2">
|
||||||
<DrawerClose asChild>
|
<DrawerClose asChild>
|
||||||
<Button variant="outline">关闭</Button>
|
<Button variant="outline">关闭</Button>
|
||||||
@@ -81,7 +109,12 @@ export function UserInfoEditor({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ProfileForm({ className, user, ...props }: React.ComponentProps<"form"> & { user: User }) {
|
function ProfileForm({ className, user, onSetPassword, onRemove, ...props }:
|
||||||
|
React.ComponentProps<"form"> & {
|
||||||
|
user: User,
|
||||||
|
onSetPassword: () => Promise<void>,
|
||||||
|
onRemove: () => Promise<void>,
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<form className={cn("grid items-start gap-4", className)} {...props}>
|
<form className={cn("grid items-start gap-4", className)} {...props}>
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
@@ -104,6 +137,10 @@ function ProfileForm({ className, user, ...props }: React.ComponentProps<"form">
|
|||||||
<Label htmlFor="phone">手机号</Label>
|
<Label htmlFor="phone">手机号</Label>
|
||||||
<Input id="phone" name="phone" defaultValue={user.phone} />
|
<Input id="phone" name="phone" defaultValue={user.phone} />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="w-full flex gap-5">
|
||||||
|
<Button type="button" variant="secondary" className="flex-1" onClick={onSetPassword}>修改密码</Button>
|
||||||
|
<Button type="button" variant="destructive" className="flex-1" onClick={onRemove}>删除用户</Button>
|
||||||
|
</div>
|
||||||
<Button type="submit">保存</Button>
|
<Button type="submit">保存</Button>
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
|
|||||||
66
tone-page-web/components/ui/alert.tsx
Normal file
66
tone-page-web/components/ui/alert.tsx
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const alertVariants = cva(
|
||||||
|
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "bg-card text-card-foreground",
|
||||||
|
destructive:
|
||||||
|
"text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
function Alert({
|
||||||
|
className,
|
||||||
|
variant,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="alert"
|
||||||
|
role="alert"
|
||||||
|
className={cn(alertVariants({ variant }), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="alert-title"
|
||||||
|
className={cn(
|
||||||
|
"col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function AlertDescription({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="alert-description"
|
||||||
|
className={cn(
|
||||||
|
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Alert, AlertTitle, AlertDescription }
|
||||||
@@ -2,3 +2,4 @@ export * from './list';
|
|||||||
export * from './get';
|
export * from './get';
|
||||||
export * from './create';
|
export * from './create';
|
||||||
export * from './update';
|
export * from './update';
|
||||||
|
export * from './set-password';
|
||||||
7
tone-page-web/lib/api/admin/user/remove.ts
Normal file
7
tone-page-web/lib/api/admin/user/remove.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import fetcher from "../../fetcher";
|
||||||
|
|
||||||
|
export async function remove(userId: string) {
|
||||||
|
return fetcher(`/admin/user/${userId}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
})
|
||||||
|
}
|
||||||
10
tone-page-web/lib/api/admin/user/set-password.ts
Normal file
10
tone-page-web/lib/api/admin/user/set-password.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import fetcher from "../../fetcher";
|
||||||
|
|
||||||
|
export async function setPassword(userId: string, password: string) {
|
||||||
|
return fetcher(`/admin/user/${userId}/password`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
password,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
"@radix-ui/react-separator": "^1.1.6",
|
"@radix-ui/react-separator": "^1.1.6",
|
||||||
"@radix-ui/react-slot": "^1.2.0",
|
"@radix-ui/react-slot": "^1.2.0",
|
||||||
"@radix-ui/react-tooltip": "^1.2.6",
|
"@radix-ui/react-tooltip": "^1.2.6",
|
||||||
|
"alert": "^6.0.2",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"dialog": "^0.3.1",
|
"dialog": "^0.3.1",
|
||||||
|
|||||||
14
tone-page-web/pnpm-lock.yaml
generated
14
tone-page-web/pnpm-lock.yaml
generated
@@ -41,6 +41,9 @@ importers:
|
|||||||
'@radix-ui/react-tooltip':
|
'@radix-ui/react-tooltip':
|
||||||
specifier: ^1.2.6
|
specifier: ^1.2.6
|
||||||
version: 1.2.6(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
version: 1.2.6(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
|
alert:
|
||||||
|
specifier: ^6.0.2
|
||||||
|
version: 6.0.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||||
class-variance-authority:
|
class-variance-authority:
|
||||||
specifier: ^0.7.1
|
specifier: ^0.7.1
|
||||||
version: 0.7.1
|
version: 0.7.1
|
||||||
@@ -1227,6 +1230,12 @@ packages:
|
|||||||
ajv@6.12.6:
|
ajv@6.12.6:
|
||||||
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
|
||||||
|
|
||||||
|
alert@6.0.2:
|
||||||
|
resolution: {integrity: sha512-Oi8u2HRNN6mzpjgKGii2Uuf9iOhyfbeUAHH/5MwnVmC8DS9GrEBjZBFpoavkNj+ZKnBr/Lqx+6YKLDKrggKfPA==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^18.0.0
|
||||||
|
react-dom: ^18.0.0
|
||||||
|
|
||||||
ansi-styles@4.3.0:
|
ansi-styles@4.3.0:
|
||||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -3509,6 +3518,11 @@ snapshots:
|
|||||||
json-schema-traverse: 0.4.1
|
json-schema-traverse: 0.4.1
|
||||||
uri-js: 4.4.1
|
uri-js: 4.4.1
|
||||||
|
|
||||||
|
alert@6.0.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||||
|
dependencies:
|
||||||
|
react: 19.1.0
|
||||||
|
react-dom: 19.1.0(react@19.1.0)
|
||||||
|
|
||||||
ansi-styles@4.3.0:
|
ansi-styles@4.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-convert: 2.0.1
|
color-convert: 2.0.1
|
||||||
|
|||||||
Reference in New Issue
Block a user