完成了一些接口
This commit is contained in:
@@ -20,6 +20,12 @@ import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { updateUser } from "@/lib/api/admin/user";
|
||||
import { AdminApi } from "@/lib/api";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
Alert,
|
||||
AlertDescription,
|
||||
AlertTitle,
|
||||
} from "@/components/ui/alert"
|
||||
import { AlertCircle } from "lucide-react";
|
||||
|
||||
export function UserInfoEditor({
|
||||
onClose,
|
||||
@@ -46,6 +52,14 @@ export function UserInfoEditor({
|
||||
}
|
||||
}
|
||||
|
||||
const handleRemove = async () => {
|
||||
|
||||
}
|
||||
|
||||
const handleSetPassword = async () => {
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<Drawer open={!!userId} onClose={onClose} >
|
||||
<DrawerContent>
|
||||
@@ -54,7 +68,11 @@ export function UserInfoEditor({
|
||||
<DrawerDescription>确保你在保存之前检查所有更改</DrawerDescription>
|
||||
</DrawerHeader>
|
||||
|
||||
{user && <ProfileForm className="px-4" user={user} onSubmit={(e) => {
|
||||
{user && <ProfileForm className="px-4"
|
||||
user={user}
|
||||
onSetPassword={handleSetPassword}
|
||||
onRemove={handleRemove}
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
const formData = new FormData(e.currentTarget);
|
||||
handleSave({
|
||||
@@ -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">
|
||||
<DrawerClose asChild>
|
||||
<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 (
|
||||
<form className={cn("grid items-start gap-4", className)} {...props}>
|
||||
<div className="grid gap-2">
|
||||
@@ -104,6 +137,10 @@ function ProfileForm({ className, user, ...props }: React.ComponentProps<"form">
|
||||
<Label htmlFor="phone">手机号</Label>
|
||||
<Input id="phone" name="phone" defaultValue={user.phone} />
|
||||
</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>
|
||||
</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 './create';
|
||||
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-slot": "^1.2.0",
|
||||
"@radix-ui/react-tooltip": "^1.2.6",
|
||||
"alert": "^6.0.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.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':
|
||||
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)
|
||||
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:
|
||||
specifier: ^0.7.1
|
||||
version: 0.7.1
|
||||
@@ -1227,6 +1230,12 @@ packages:
|
||||
ajv@6.12.6:
|
||||
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:
|
||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -3509,6 +3518,11 @@ snapshots:
|
||||
json-schema-traverse: 0.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:
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
|
||||
Reference in New Issue
Block a user