Restructure docs navigation for channel and widget guides
This commit is contained in:
+1
-1
Submodule docs updated: 751a56bdfd...b99f88541a
@@ -15,6 +15,7 @@ import { useAppLocale, useI18n } from "@/i18n/provider"
|
|||||||
import { Status } from "@/lib/generated/enums"
|
import { Status } from "@/lib/generated/enums"
|
||||||
import { getRoleDisplayName } from "@/lib/role-i18n"
|
import { getRoleDisplayName } from "@/lib/role-i18n"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
import { ImageInput } from "@/components/image-input"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Checkbox } from "@/components/ui/checkbox"
|
import { Checkbox } from "@/components/ui/checkbox"
|
||||||
@@ -119,13 +120,7 @@ function CreateUserDrawerBody({
|
|||||||
z.object({
|
z.object({
|
||||||
username: z.string().trim().min(1, t("user.usernameRequired")),
|
username: z.string().trim().min(1, t("user.usernameRequired")),
|
||||||
nickname: z.string().trim(),
|
nickname: z.string().trim(),
|
||||||
avatar: z
|
avatar: z.string().trim(),
|
||||||
.string()
|
|
||||||
.trim()
|
|
||||||
.refine(
|
|
||||||
(value) => value.length === 0 || /^https?:\/\/\S+$/i.test(value),
|
|
||||||
t("user.avatarInvalid")
|
|
||||||
),
|
|
||||||
mobile: z
|
mobile: z
|
||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
@@ -233,14 +228,22 @@ function CreateUserDrawerBody({
|
|||||||
<FieldError errors={[errors.nickname]} />
|
<FieldError errors={[errors.nickname]} />
|
||||||
</FieldContent>
|
</FieldContent>
|
||||||
</Field>
|
</Field>
|
||||||
<Field data-invalid={!!errors.avatar}>
|
<Field className="min-h-32" data-invalid={!!errors.avatar}>
|
||||||
<FieldLabel htmlFor="create-avatar">{t("user.avatar")}</FieldLabel>
|
<FieldLabel>{t("user.avatar")}</FieldLabel>
|
||||||
<FieldContent>
|
<FieldContent>
|
||||||
<Input
|
<Controller
|
||||||
id="create-avatar"
|
control={control}
|
||||||
placeholder={t("user.avatarCreatePlaceholder")}
|
name="avatar"
|
||||||
aria-invalid={!!errors.avatar}
|
render={({ field }) => (
|
||||||
{...register("avatar")}
|
<ImageInput
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
|
disabled={saving}
|
||||||
|
prefix="avatar"
|
||||||
|
placeholder={t("user.avatarCreatePlaceholder")}
|
||||||
|
className="size-16 rounded-full"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
<FieldError errors={[errors.avatar]} />
|
<FieldError errors={[errors.avatar]} />
|
||||||
</FieldContent>
|
</FieldContent>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useEffect, useMemo, useState } from "react"
|
import { useEffect, useMemo, useState } from "react"
|
||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { Resolver, useForm } from "react-hook-form"
|
import { Controller, Resolver, useForm } from "react-hook-form"
|
||||||
import { z } from "zod/v4"
|
import { z } from "zod/v4"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
fetchUserDetail,
|
fetchUserDetail,
|
||||||
} from "@/lib/api/admin"
|
} from "@/lib/api/admin"
|
||||||
import { useI18n } from "@/i18n/provider"
|
import { useI18n } from "@/i18n/provider"
|
||||||
|
import { ImageInput } from "@/components/image-input"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import {
|
import {
|
||||||
Drawer,
|
Drawer,
|
||||||
@@ -121,13 +122,7 @@ function UserEditDrawerBody({
|
|||||||
() =>
|
() =>
|
||||||
z.object({
|
z.object({
|
||||||
nickname: z.string().trim().min(1, t("user.nicknameRequired")),
|
nickname: z.string().trim().min(1, t("user.nicknameRequired")),
|
||||||
avatar: z
|
avatar: z.string().trim(),
|
||||||
.string()
|
|
||||||
.trim()
|
|
||||||
.refine(
|
|
||||||
(value) => value.length === 0 || /^https?:\/\/\S+$/i.test(value),
|
|
||||||
t("user.avatarInvalid")
|
|
||||||
),
|
|
||||||
mobile: z
|
mobile: z
|
||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
@@ -155,6 +150,7 @@ function UserEditDrawerBody({
|
|||||||
defaultValues: emptyForm,
|
defaultValues: emptyForm,
|
||||||
})
|
})
|
||||||
const {
|
const {
|
||||||
|
control,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
reset,
|
reset,
|
||||||
register,
|
register,
|
||||||
@@ -220,14 +216,22 @@ function UserEditDrawerBody({
|
|||||||
<FieldError errors={[errors.nickname]} />
|
<FieldError errors={[errors.nickname]} />
|
||||||
</FieldContent>
|
</FieldContent>
|
||||||
</Field>
|
</Field>
|
||||||
<Field data-invalid={!!errors.avatar}>
|
<Field className="min-h-32" data-invalid={!!errors.avatar}>
|
||||||
<FieldLabel htmlFor="user-avatar">{t("user.avatar")}</FieldLabel>
|
<FieldLabel>{t("user.avatar")}</FieldLabel>
|
||||||
<FieldContent>
|
<FieldContent>
|
||||||
<Input
|
<Controller
|
||||||
id="user-avatar"
|
control={control}
|
||||||
placeholder={t("user.avatarPlaceholder")}
|
name="avatar"
|
||||||
aria-invalid={!!errors.avatar}
|
render={({ field }) => (
|
||||||
{...register("avatar")}
|
<ImageInput
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
|
disabled={saving || loading}
|
||||||
|
prefix="avatar"
|
||||||
|
placeholder={t("user.avatarPlaceholder")}
|
||||||
|
className="size-16 rounded-full"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
<FieldError errors={[errors.avatar]} />
|
<FieldError errors={[errors.avatar]} />
|
||||||
</FieldContent>
|
</FieldContent>
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export function ImageInput({
|
|||||||
const isDisabled = disabled || uploading
|
const isDisabled = disabled || uploading
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("relative", className)}>
|
<div className="relative w-fit">
|
||||||
<input
|
<input
|
||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
type="file"
|
type="file"
|
||||||
@@ -96,7 +96,8 @@ export function ImageInput({
|
|||||||
"group relative flex size-24 cursor-pointer items-center justify-center overflow-hidden rounded-lg border-2 border-dashed border-input bg-muted transition-colors",
|
"group relative flex size-24 cursor-pointer items-center justify-center overflow-hidden rounded-lg border-2 border-dashed border-input bg-muted transition-colors",
|
||||||
"hover:border-primary hover:bg-muted/50",
|
"hover:border-primary hover:bg-muted/50",
|
||||||
"focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:outline-none",
|
"focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:outline-none",
|
||||||
isDisabled && "cursor-not-allowed opacity-50"
|
isDisabled && "cursor-not-allowed opacity-50",
|
||||||
|
className
|
||||||
)}
|
)}
|
||||||
tabIndex={isDisabled ? -1 : 0}
|
tabIndex={isDisabled ? -1 : 0}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
|
|||||||
@@ -926,7 +926,7 @@
|
|||||||
"emptyRows": "No matching users",
|
"emptyRows": "No matching users",
|
||||||
"nicknameRequired": "Enter a nickname.",
|
"nicknameRequired": "Enter a nickname.",
|
||||||
"usernameRequired": "Enter a username.",
|
"usernameRequired": "Enter a username.",
|
||||||
"avatarInvalid": "Avatar URL must start with http or https.",
|
"avatarInvalid": "Invalid avatar.",
|
||||||
"mobileInvalid": "Enter a valid phone number.",
|
"mobileInvalid": "Enter a valid phone number.",
|
||||||
"emailInvalid": "Enter a valid email address.",
|
"emailInvalid": "Enter a valid email address.",
|
||||||
"editTitle": "Edit User",
|
"editTitle": "Edit User",
|
||||||
@@ -934,8 +934,8 @@
|
|||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
"nickname": "Nickname",
|
"nickname": "Nickname",
|
||||||
"nicknamePlaceholder": "Enter nickname",
|
"nicknamePlaceholder": "Enter nickname",
|
||||||
"avatar": "Avatar URL",
|
"avatar": "Avatar",
|
||||||
"avatarPlaceholder": "Enter avatar URL",
|
"avatarPlaceholder": "Upload avatar",
|
||||||
"mobile": "Mobile",
|
"mobile": "Mobile",
|
||||||
"mobilePlaceholder": "Enter mobile number",
|
"mobilePlaceholder": "Enter mobile number",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
@@ -948,7 +948,7 @@
|
|||||||
"username": "Username",
|
"username": "Username",
|
||||||
"usernamePlaceholder": "Login name, required",
|
"usernamePlaceholder": "Login name, required",
|
||||||
"nicknameCreatePlaceholder": "Optional, defaults to username",
|
"nicknameCreatePlaceholder": "Optional, defaults to username",
|
||||||
"avatarCreatePlaceholder": "Optional, http(s) URL",
|
"avatarCreatePlaceholder": "Upload avatar",
|
||||||
"optional": "Optional",
|
"optional": "Optional",
|
||||||
"remark": "Notes",
|
"remark": "Notes",
|
||||||
"rolesOptional": "Roles (optional)",
|
"rolesOptional": "Roles (optional)",
|
||||||
|
|||||||
@@ -926,7 +926,7 @@
|
|||||||
"emptyRows": "没有匹配的用户数据",
|
"emptyRows": "没有匹配的用户数据",
|
||||||
"nicknameRequired": "昵称不能为空",
|
"nicknameRequired": "昵称不能为空",
|
||||||
"usernameRequired": "用户名不能为空",
|
"usernameRequired": "用户名不能为空",
|
||||||
"avatarInvalid": "头像地址必须是 http 或 https 链接",
|
"avatarInvalid": "头像不合法",
|
||||||
"mobileInvalid": "手机号格式不正确",
|
"mobileInvalid": "手机号格式不正确",
|
||||||
"emailInvalid": "邮箱格式不正确",
|
"emailInvalid": "邮箱格式不正确",
|
||||||
"editTitle": "修改用户",
|
"editTitle": "修改用户",
|
||||||
@@ -934,8 +934,8 @@
|
|||||||
"loading": "加载中...",
|
"loading": "加载中...",
|
||||||
"nickname": "昵称",
|
"nickname": "昵称",
|
||||||
"nicknamePlaceholder": "请输入昵称",
|
"nicknamePlaceholder": "请输入昵称",
|
||||||
"avatar": "头像地址",
|
"avatar": "头像",
|
||||||
"avatarPlaceholder": "请输入头像 URL",
|
"avatarPlaceholder": "上传头像",
|
||||||
"mobile": "手机号",
|
"mobile": "手机号",
|
||||||
"mobilePlaceholder": "请输入手机号",
|
"mobilePlaceholder": "请输入手机号",
|
||||||
"email": "邮箱",
|
"email": "邮箱",
|
||||||
@@ -948,7 +948,7 @@
|
|||||||
"username": "用户名",
|
"username": "用户名",
|
||||||
"usernamePlaceholder": "登录名,必填",
|
"usernamePlaceholder": "登录名,必填",
|
||||||
"nicknameCreatePlaceholder": "可选,默认同用户名",
|
"nicknameCreatePlaceholder": "可选,默认同用户名",
|
||||||
"avatarCreatePlaceholder": "可选,http(s) 链接",
|
"avatarCreatePlaceholder": "上传头像",
|
||||||
"optional": "可选",
|
"optional": "可选",
|
||||||
"remark": "备注",
|
"remark": "备注",
|
||||||
"rolesOptional": "角色(可选)",
|
"rolesOptional": "角色(可选)",
|
||||||
|
|||||||
Reference in New Issue
Block a user