diff --git a/README.md b/README.md
index 5f9fbfa..4fc0636 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,9 @@
## TODO
+验证码读秒用 store 保存到本地,(全局共享读秒时间)?
+
+网页标题根据实际页面变化
+
检查时间范围选择,限定到一定范围内
将翻页操作反映在路由历史中,可以通过后退返回到上一个翻页状态
@@ -21,3 +25,6 @@
- [ ] 提取记录
- [ ] 使用记录
+检查扩大服务端组件边界
+
+检查 Card 替换 section 或 div
diff --git a/src/actions/base.ts b/src/actions/base.ts
index 8b408d4..ac5d0b5 100644
--- a/src/actions/base.ts
+++ b/src/actions/base.ts
@@ -163,6 +163,7 @@ async function postCall(rawResp: Promise>) {
].some(item => item.test(pathname))
if (match && !resp.success && resp.status === 401) {
+ console.log("!!!!!!!!!redirect", resp.message)
redirect(pathname === '/' ? '/login' : `/login?redirect=${pathname}`)
}
diff --git a/src/actions/user.ts b/src/actions/user.ts
index 81cb59b..282b0ec 100644
--- a/src/actions/user.ts
+++ b/src/actions/user.ts
@@ -1,5 +1,4 @@
'use server'
-
import {callByUser, callPublic} from '@/actions/base'
export async function RechargeByAlipay(props: {
@@ -51,3 +50,20 @@ export async function IdentifyCallback(props: {
message: string
}>('/api/user/identify/callback', props)
}
+
+export async function update(props: {
+ username: string
+ email: string
+ contact_qq: string
+ contact_wechat: string
+}) {
+ return await callByUser('/api/user/update', props)
+}
+
+export async function updatePassword(props: {
+ phone: string
+ code: string
+ password: string
+}) {
+ return await callByUser('/api/user/update/password', props)
+}
diff --git a/src/app/admin/identify/page.tsx b/src/app/admin/identify/page.tsx
index a8bba07..107961f 100644
--- a/src/app/admin/identify/page.tsx
+++ b/src/app/admin/identify/page.tsx
@@ -18,6 +18,8 @@ import personal from './_assets/personal.webp'
import step1 from './_assets/step1.webp'
import step2 from './_assets/step2.webp'
import step3 from './_assets/step3.webp'
+import {Card, CardContent, CardDescription, CardHeader, CardTitle} from '@/components/ui/card'
+import {WorkflowIcon} from 'lucide-react'
export type IdentifyPageProps = {}
@@ -180,15 +182,21 @@ export default function IdentifyPage(props: IdentifyPageProps) {
-
- 操作引导
- 为响应国家相关规定,使用HTTP代理需完成实名认证。认证服务由支付宝提供,您的个人信息将受到严格保护,仅用于账户安全认证
-
+
+
+
+ 操作引导
+
+
+
+
+ 为响应国家相关规定,使用HTTP代理需完成实名认证。认证服务由支付宝提供,您的个人信息将受到严格保护,仅用于账户安全认证
+
-
- 01
- 注册账号
-
+
+ 01
+ 注册账号
+
@@ -198,10 +206,10 @@ export default function IdentifyPage(props: IdentifyPageProps) {
)}>
-
- 02
- 实名认证
-
+
+ 02
+ 实名认证
+
@@ -211,14 +219,14 @@ export default function IdentifyPage(props: IdentifyPageProps) {
)}>
-
- 03
- 充值、支付
-
+
+ 03
+ 充值、支付
+
-
-
+
+
)
}
diff --git a/src/app/admin/profile/page.tsx b/src/app/admin/profile/page.tsx
index e853db0..6a808b6 100644
--- a/src/app/admin/profile/page.tsx
+++ b/src/app/admin/profile/page.tsx
@@ -1,651 +1,461 @@
'use client'
-import {useEffect, useState, useContext, useRef} from 'react'
+import {useEffect, useRef, useState} from 'react'
import Page from '@/components/page'
-import {Tabs, TabsContent, TabsList, TabsTrigger} from '@/components/ui/tabs'
-import {Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle} from '@/components/ui/card'
-import {Form, FormField, FormLabel} from '@/components/ui/form'
+import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card'
+import {Form, FormField} from '@/components/ui/form'
import {Button} from '@/components/ui/button'
-import {Input} from '@/components/ui/input'
import {useForm} from 'react-hook-form'
import {zodResolver} from '@hookform/resolvers/zod'
import * as z from 'zod'
-import {StoreContext, useProfileStore} from '@/components/providers/StoreProvider'
-import {Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger} from '@/components/ui/dialog'
+import {useProfileStore} from '@/components/providers/StoreProvider'
import {toast} from 'sonner'
-import {Alert, AlertDescription, AlertTitle} from '@/components/ui/alert'
-import {CheckCircle, AlertCircle, Shield, Wallet, CreditCard, QrCode, UserIcon} from 'lucide-react'
+import {CheckCircle, QrCodeIcon} from 'lucide-react'
import * as qrcode from 'qrcode'
-import Link from 'next/link'
-import RechargeModal from '@/components/composites/purchase/_client/recharge'
+import Image from 'next/image'
+import banner from '@/app/admin/identify/_assets/banner.webp'
+import {Input} from '@/components/ui/input'
+import {merge} from '@/lib/utils'
import {User} from '@/lib/models'
-import { Label } from '@/components/ui/label'
+import {update, updatePassword} from '@/actions/user'
+import {
+ Dialog, DialogContent,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from '@/components/ui/dialog'
+import {sendSMS} from '@/actions/verify'
+import RechargeModal from '@/components/composites/recharge'
export type ProfilePageProps = {}
export default function ProfilePage(props: ProfilePageProps) {
- const profile = useProfileStore(store => store.profile)
- const refreshProfile = useProfileStore(store => store.refreshProfile)
- // 默认选中的Tab
- const [activeTab, setActiveTab] = useState('basic')
+ const profile = useProfileStore(store => store.profile)
+
+ // ======================
+ // render
+ // ======================
if (!profile) {
return (
-
-
-
- 加载中
- 正在加载个人信息,请稍候...
-
+
)
}
return (
-
-
-
-
-
- 基本信息
- 安全设置
- 余额管理
- 实名认证
-
+
+
+ {/* banner */}
+
+
+ 蓝狐HTTP邀请您参与【先测后买】服务
+ 为了保障您的账户安全,请先完成实名认证,即可获取福利套餐测试资格
+
-
-
-
+ {/* 块信息 */}
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* 侧边栏:客服经理信息 */}
-
-
+
-
- 账户概览
-
+ 帐号余额(元)
-
-
-
用户名
-
{profile.name || profile.username}
-
-
-
-
手机号
-
{profile.phone ? maskPhone(profile.phone) : '未设置'}
-
-
-
-
账户余额
-
¥{profile.balance || 0}
-
-
-
-
实名认证
-
- {profile.id_token ? (
- <> 已认证>
- ) : (
- <> 未认证>
- )}
-
-
+
+ {profile?.balance}
+
- {profile.contact_wechat && (
-
-
-
- 专属客服经理
-
-
-
-
-
-
- 扫描上方二维码添加客服经理微信
获取更多帮助与支持
+
+
+ 实名认证
+
+
+ {!profile?.id_token
+ ? <>
+ 为了保障您的账户安全和正常使用服务,请您尽快完成实名认证
+
+ >
+ : <>
+
+ {profile.name}
+ {profile.id_no}
-
-
-
- )}
+
+
+ 已认证
+
+ >}
+
+
+
+
+
+
+ {/* 安全信息 */}
+
+
+ {/* 基本信息 */}
+
+
+ {/* 侧边栏:客服经理信息 */}
+
+
)
}
-// 基本信息组件
-function BasicInfoTab(props: {
+function Aftersale(props: {
profile: User
- refreshProfile: () => Promise
}) {
- const {profile, refreshProfile} = props
-
- const basicInfoSchema = z.object({
- name: z.string().min(2, '名称至少需要2个字符'),
- email: z.string().email('请输入有效的邮箱地址').or(z.string().length(0)),
- })
-
- type BasicInfoShema = z.infer
-
- const form = useForm({
- resolver: zodResolver(basicInfoSchema),
- defaultValues: {
- name: profile.name || '',
- email: profile.email || '',
- },
- })
-
- const [isSubmitting, setIsSubmitting] = useState(false)
-
- const onSubmit = async (data: BasicInfoShema) => {
- setIsSubmitting(true)
- try {
- // 这里会调用更新用户信息的API
- // 示例: await updateUserProfile(data)
-
- toast.success('基本信息更新成功')
- await refreshProfile()
- }
- catch (error) {
- toast.error('更新失败', {
- description: (error as Error).message || '请稍后重试',
- })
- }
- finally {
- setIsSubmitting(false)
- }
- }
-
- return (
-
- )
-}
-
-// 安全设置组件
-function SecurityTab(props: {
- profile: User
- refreshProfile: () => Promise
-}) {
- const {profile, refreshProfile} = props
-
- const [showPhoneDialog, setShowPhoneDialog] = useState(false)
- const [showPasswordDialog, setShowPasswordDialog] = useState(false)
-
- return (
-
-
-
-
手机号
-
- {profile.phone ? maskPhone(profile.phone) : '未设置手机号'}
-
-
-
-
-
-
-
-
密码
-
定期修改密码可以保障您的账户安全
-
-
-
-
-
-
- {/* 修改手机号对话框 */}
-
-
- {/* 修改密码对话框 */}
-
-
- )
-}
-
-// 余额管理组件
-function BalanceTab(props: {
- profile: User
- refreshProfile: () => Promise
-}) {
- const {profile} = props
-
- // 交易历史记录示例数据
- const transactions = [
- {id: 1, type: '充值', amount: 100, date: '2025-04-18', status: '成功'},
- {id: 2, type: '购买套餐', amount: -50, date: '2025-04-15', status: '成功'},
- {id: 3, type: '系统赠送', amount: 10, date: '2025-04-10', status: '成功'},
- ]
-
- return (
-
-
-
-
当前余额: ¥{profile.balance || 0}
-
您可以随时充值或查看交易记录
-
-
-
-
-
-
近期交易记录
-
-
-
-
- {transactions.map(tx => (
-
-
{tx.type}
-
= 0 ? 'text-green-600' : 'text-red-600'}>
- {tx.amount >= 0 ? `+${tx.amount}` : tx.amount}
-
-
{tx.date}
-
{tx.status}
-
- ))}
-
-
-
-
-
-
-
-
- )
-}
-
-// 实名认证组件
-function IdentifyTab(props: {
- profile: User
- refreshProfile: () => Promise
-}) {
- const {profile} = props
-
- return (
-
- {profile.id_token ? (
-
-
-
您已完成实名认证
-
认证信息已通过验证,可以正常使用所有功能
-
-
-
-
认证姓名
-
{maskName(profile.name)}
-
-
-
身份证号
-
{maskIdNumber(profile.id_no)}
-
-
-
- ) : (
-
-
-
您尚未完成实名认证
-
- 根据相关法律法规要求,使用HTTP代理服务需要先完成实名认证
-
-
-
-
-
-
- )}
-
-
-
- 重要提示
-
- 为响应国家相关规定,使用HTTP代理需完成实名认证。
- 认证服务由支付宝提供,您的个人信息将受到严格保护,仅用于账户安全认证。
-
-
-
- )
-}
-
-// 修改手机号对话框
-function ChangePhoneDialog(props: {
- open: boolean
- onOpenChange: (open: boolean) => void
- currentPhone?: string
- refreshProfile: () => Promise
-}) {
- const {open, onOpenChange, currentPhone, refreshProfile} = props
-
- const phoneChangeSchema = z.object({
- newPhone: z.string().length(11, '请输入11位手机号码'),
- verifyCode: z.string().min(4, '请输入验证码'),
- })
-
- type PhoneChangeSchema = z.infer
-
- const form = useForm({
- resolver: zodResolver(phoneChangeSchema),
- defaultValues: {
- newPhone: '',
- verifyCode: '',
- },
- })
-
- const [isSubmitting, setIsSubmitting] = useState(false)
- const [countdown, setCountdown] = useState(0)
-
- const onSubmit = async (data: PhoneChangeSchema) => {
- setIsSubmitting(true)
- try {
- // 这里调用修改手机号的API
- // 示例: await changePhone(data.newPhone, data.verifyCode)
-
- toast.success('手机号更新成功')
- await refreshProfile()
- onOpenChange(false)
- }
- catch (error) {
- toast.error('更新失败', {
- description: (error as Error).message || '请稍后重试',
- })
- }
- finally {
- setIsSubmitting(false)
- }
- }
-
- const sendVerifyCode = () => {
- const newPhone = form.getValues('newPhone')
- if (!newPhone || newPhone.length !== 11) {
- form.setError('newPhone', {message: '请输入有效的手机号码'})
- return
- }
-
- // 这里调用发送验证码的API
- // 示例: await sendSmsCode(newPhone)
- toast.success('验证码已发送')
-
- // 开始倒计时
- setCountdown(60)
- const timer = setInterval(() => {
- setCountdown(prev => {
- if (prev <= 1) {
- clearInterval(timer)
- return 0
- }
- return prev - 1
- })
- }, 1000)
- }
-
- return (
-
- )
-}
-
-// 修改密码对话框
-function ChangePasswordDialog(props: {
- open: boolean
- onOpenChange: (open: boolean) => void
- refreshProfile: () => Promise
-}) {
- const {open, onOpenChange, refreshProfile} = props
-
- const passwordChangeSchema = z.object({
- oldPassword: z.string().min(6, '密码至少需要6个字符'),
- newPassword: z.string().min(6, '密码至少需要6个字符'),
- confirmPassword: z.string().min(6, '密码至少需要6个字符'),
- }).refine(data => data.newPassword === data.confirmPassword, {
- message: '两次输入的新密码不一致',
- path: ['confirmPassword'],
- })
-
- type PasswordChangeSchema = z.infer
-
- const form = useForm({
- resolver: zodResolver(passwordChangeSchema),
- defaultValues: {
- oldPassword: '',
- newPassword: '',
- confirmPassword: '',
- },
- })
-
- const [isSubmitting, setIsSubmitting] = useState(false)
-
- const onSubmit = async (data: PasswordChangeSchema) => {
- setIsSubmitting(true)
- try {
- // 这里调用修改密码的API
- // 示例: await changePassword(data.oldPassword, data.newPassword)
-
- toast.success('密码修改成功')
- onOpenChange(false)
- }
- catch (error) {
- toast.error('修改失败', {
- description: (error as Error).message || '请稍后重试',
- })
- }
- finally {
- setIsSubmitting(false)
- }
- }
-
- return (
-
- )
-}
-
-// 客服经理二维码组件
-function ServiceManagerQRCode(props: {
- wechat?: string
-}) {
- const {wechat} = props
+ const admin = props.profile.admin_id
const canvasRef = useRef(null)
useEffect(() => {
- if (wechat && canvasRef.current) {
- qrcode.toCanvas(canvasRef.current, wechat, {
+ if (admin && canvasRef.current) {
+ qrcode.toCanvas(canvasRef.current, String(admin), {
width: 180,
margin: 0,
}).catch(err => {
console.error(err)
})
}
- }, [wechat])
+ }, [admin])
return (
-
-
+
+
+
+ 关于售后
+
+
+
+
+
+
+ 1.全国100万+动态IP代理资源免费测试,先测后买让您安心使用。
+
+
+
+ 2.注册即享新人福利,专业的客户经理,多维度为您提供在线代理相关答疑
+
+
+
+ 3.1V1专属售后答疑,技术团队7*24小时在线支持提供专属解决方案
+
+
+
+
+
您的专属客服经理
+
+
+
+
+ 扫描上方二维码添加客服经理微信
获取更多帮助与支持
+
+
+
+
+ )
+}
+
+function BasicForm(props: {
+ profile: User
+}) {
+
+ const schema = z.object({
+ username: z.string(),
+ email: z.string().email('请输入正确的邮箱'),
+ contact_qq: z.string(),
+ contact_wechat: z.string(),
+ })
+ type Schema = z.infer
+ const form = useForm({
+ resolver: zodResolver(schema),
+ defaultValues: {
+ username: props.profile.username || '',
+ email: props.profile.email || '',
+ contact_qq: props.profile.contact_qq || '',
+ contact_wechat: props.profile.contact_wechat || '',
+ },
+ })
+ const handler = form.handleSubmit(async value => {
+ try {
+ const resp = await update(value)
+ if (!resp.success) {
+ throw new Error(resp.message)
+ }
+
+ await refreshProfile()
+ toast.success(`保存成功`)
+ }
+ catch (e) {
+ console.error(e)
+ toast.error(`保存失败`, {
+ description: e instanceof Error ? e.message : String(e),
+ })
+ }
+ })
+
+ const refreshProfile = useProfileStore(store => store.refreshProfile)
+
+ return (
+
)
}
-// 工具函数:遮盖手机号
-function maskPhone(phone?: string) {
- if (!phone) return ''
- return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
-}
+function PasswordForm(props: {
+ profile: User
+}) {
-// 工具函数:遮盖姓名
-function maskName(name?: string) {
- if (!name) return ''
- if (name.length <= 2) {
- return name.charAt(0) + '*'
+ // ======================
+ // open
+ // ======================
+
+ const [open, setOpen] = useState(false)
+
+ // ======================
+ // form
+ // ======================
+
+ const schema = z.object({
+ phone: z.string().regex(/^1\d{10}$/, `请输入正确的手机号`),
+ captcha: z.string().nonempty('请输入验证码'),
+ code: z.string().regex(/^\d{6}$/, `请输入正确的验证码`),
+ password: z.string().min(6, `密码至少6位`),
+ confirm_password: z.string(),
+ }).refine(data => data.password === data.confirm_password, {
+ message: '两次输入的密码不一致',
+ path: ['confirm_password'],
+ })
+ type Schema = z.infer
+
+ const form = useForm({
+ resolver: zodResolver(schema),
+ defaultValues: {
+ phone: '',
+ captcha: '',
+ code: '',
+ password: '',
+ confirm_password: '',
+ },
+ })
+ const handler = form.handleSubmit(async value => {
+ try {
+ const resp = await updatePassword({
+ phone: value.phone,
+ code: value.code,
+ password: value.password,
+ })
+ if (!resp.success) {
+ throw new Error(resp.message)
+ }
+
+ toast.success(`保存成功`)
+ setOpen(false)
+ }
+ catch (e) {
+ console.error(e)
+ toast.error(`保存失败`, {
+ description: e instanceof Error ? e.message : String(e),
+ })
+ }
+ })
+
+ // ======================
+ // phone code
+ // ======================
+
+ const [captchaUrl, setCaptchaUrl] = useState(`/captcha?t=${new Date().getTime()}`)
+ const [captchaWait, setCaptchaWait] = useState(0)
+ const interval = useRef(null)
+
+ const refreshCaptcha = () => {
+ setCaptchaUrl(`/captcha?t=${new Date().getTime()}`)
}
- const stars = '*'.repeat(name.length - 2)
- return name.charAt(0) + stars + name.charAt(name.length - 1)
-}
-// 工具函数:遮盖身份证号
-function maskIdNumber(idNumber?: string) {
- if (!idNumber) return ''
- return idNumber.replace(/^(.{4})(.*)(.{4})$/, '$1**********$3')
+ const sendVerifier = async () => {
+ const result = await form.trigger(['phone', 'captcha'])
+ if (!result) {
+ return
+ }
+
+ const {phone, captcha} = form.getValues()
+ const resp = await sendSMS({phone, captcha})
+ if (!resp.success) {
+ toast.error(resp.message)
+ refreshCaptcha()
+ return
+ }
+
+ setCaptchaWait(60)
+ interval.current = setInterval(() => {
+ setCaptchaWait(wait => {
+ if (wait <= 1) {
+ clearInterval(interval.current!)
+ return 0
+ }
+ return wait - 1
+ })
+ }, 1000)
+
+ toast.success(`验证码已发送,请注意查收`)
+ }
+
+ // ======================
+ // render
+ // ======================
+
+ return (
+
+ )
}
diff --git a/src/components/composites/purchase/_client/right.tsx b/src/components/composites/purchase/_client/right.tsx
index 1504797..d63d784 100644
--- a/src/components/composites/purchase/_client/right.tsx
+++ b/src/components/composites/purchase/_client/right.tsx
@@ -9,7 +9,7 @@ import alipay from '@/components/composites/purchase/_assets/alipay.svg'
import wechat from '@/components/composites/purchase/_assets/wechat.svg'
import balance from '@/components/composites/purchase/_assets/balance.svg'
import {useProfileStore} from '@/components/providers/StoreProvider'
-import RechargeModal from '@/components/composites/purchase/_client/recharge'
+import RechargeModal from '@/components/composites/recharge'
import Pay from '@/components/composites/purchase/_client/pay'
import {buttonVariants} from '@/components/ui/button'
import Link from 'next/link'
diff --git a/src/components/composites/purchase/_client/recharge.tsx b/src/components/composites/recharge/index.tsx
similarity index 98%
rename from src/components/composites/purchase/_client/recharge.tsx
rename to src/components/composites/recharge/index.tsx
index 4de604b..9e0b374 100644
--- a/src/components/composites/purchase/_client/recharge.tsx
+++ b/src/components/composites/recharge/index.tsx
@@ -17,11 +17,11 @@ import {zodResolver} from '@hookform/resolvers/zod'
import {toast} from 'sonner'
import wechat from '@/components/composites/purchase/_assets/wechat.svg'
import alipay from '@/components/composites/purchase/_assets/alipay.svg'
-import {useContext, useRef, useState} from 'react'
+import {useRef, useState} from 'react'
import {Loader} from 'lucide-react'
import {RechargeByAlipay, RechargeByAlipayConfirm, RechargeByWechat, RechargeByWechatConfirm} from '@/actions/user'
import * as qrcode from 'qrcode'
-import {StoreContext, useProfileStore} from '@/components/providers/StoreProvider'
+import {useProfileStore} from '@/components/providers/StoreProvider'
const schema = zod.object({
method: zod.enum(['alipay', 'wechat']),
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
index c20a3e4..0246d86 100644
--- a/src/components/ui/button.tsx
+++ b/src/components/ui/button.tsx
@@ -3,33 +3,33 @@ import {merge} from '@/lib/utils'
import {cva} from 'class-variance-authority'
const buttonVariants = cva(
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+ 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
{
variants: {
variant: {
default:
- "bg-primary text-primary-foreground shadow hover:bg-primary/90",
+ 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
destructive:
- "bg-fail text-fail-foreground shadow-sm hover:bg-destructive/90",
+ 'bg-fail text-fail-foreground shadow-sm hover:bg-destructive/90',
outline:
- "border border-input shadow-sm hover:bg-secondary hover:text-secondary-foreground bg-card",
+ 'border border-input shadow-sm hover:bg-secondary hover:text-secondary-foreground bg-card',
secondary:
- "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
- ghost: "hover:bg-secondary hover:text-secondary-foreground",
- link: "text-primary underline-offset-4 hover:underline",
+ 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
+ ghost: 'hover:bg-secondary hover:text-secondary-foreground',
+ link: 'text-primary underline-offset-4 hover:underline',
},
size: {
- default: "h-9 px-4 py-2",
- sm: "h-8 rounded-md px-3 text-xs",
- lg: "h-10 rounded-md px-8",
- icon: "h-9 w-9",
+ default: 'h-9 px-4 py-2',
+ sm: 'h-8 rounded-md px-3 text-xs',
+ lg: 'h-10 rounded-md px-8',
+ icon: 'h-9 w-9',
},
},
defaultVariants: {
- variant: "default",
- size: "default",
+ variant: 'default',
+ size: 'default',
},
- }
+ },
)
type ButtonProps = React.ComponentProps<'button'> & {
@@ -40,6 +40,7 @@ function Button(rawProps: ButtonProps) {
const {className, theme, ...props} = rawProps
return (
)
}
diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx
index 1db263a..a976fa6 100644
--- a/src/components/ui/card.tsx
+++ b/src/components/ui/card.tsx
@@ -1,81 +1,85 @@
-import * as React from "react"
+import * as React from 'react'
-import { merge } from "@/lib/utils"
+import {merge} from '@/lib/utils'
-function Card({ className, ...props }: React.ComponentProps<"div">) {
+function Card({className, ...props}: React.ComponentProps<'div'>) {
return (
-
)
}
-function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
+function CardHeader({className, ...props}: React.ComponentProps<'div'>) {
return (
-
)
}
-function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
+function CardTitle({className, ...props}: React.ComponentProps<'div'>) {
return (
-
)
}
-function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
+function CardDescription({className, ...props}: React.ComponentProps<'div'>) {
return (
-
)
}
-function CardAction({ className, ...props }: React.ComponentProps<"div">) {
+function CardAction({className, ...props}: React.ComponentProps<'div'>) {
return (
)
}
-function CardContent({ className, ...props }: React.ComponentProps<"div">) {
+function CardContent({className, ...props}: React.ComponentProps<'div'>) {
return (
)
}
-function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
+function CardFooter({className, ...props}: React.ComponentProps<'div'>) {
return (
)
diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx
index e68fd56..86514fd 100644
--- a/src/components/ui/dialog.tsx
+++ b/src/components/ui/dialog.tsx
@@ -124,12 +124,10 @@ function DialogDescription({
export {
Dialog,
DialogClose,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogOverlay,
- DialogPortal,
- DialogTitle,
DialogTrigger,
+ DialogHeader,
+ DialogTitle,
+ DialogDescription,
+ DialogContent,
+ DialogFooter,
}
diff --git a/src/components/ui/form.tsx b/src/components/ui/form.tsx
index 8a7b539..34f1428 100644
--- a/src/components/ui/form.tsx
+++ b/src/components/ui/form.tsx
@@ -48,16 +48,21 @@ function Form(rawProps: FormProps) {
type FormFieldProps<
V extends FieldValues = FieldValues,
N extends FieldPath = FieldPath,
-> = {
+> = Omit, 'control' | 'render'> & Omit, 'children'> & {
label?: ReactNode
- className?: string
+ description?: ReactNode
children: (props: {
id: string
field: ControllerRenderProps
fieldState: ControllerFieldState
formState: UseFormStateReturn
}) => ReactNode
-} & Omit, 'control' | 'render'>
+ classNames?: {
+ label?: string
+ description?: string
+ message?: string
+ }
+}
function FormField<
V extends FieldValues = FieldValues,
@@ -68,12 +73,15 @@ function FormField<
return (
name={props.name} control={form.control} render={({field, fieldState, formState}) => (
+
+ {/* label */}
{!!props.label &&
-
+
{props.label}
}
+ {/* control */}
+ {/* description */}
+ {!!props.description && (
+
+ {props.description}
+
+ )}
+
+ {/* message */}
{!fieldState.error ? null : (
-
+
)}
)}/>
diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx
index d9c0abb..4fb21ee 100644
--- a/src/components/ui/input.tsx
+++ b/src/components/ui/input.tsx
@@ -10,7 +10,7 @@ function Input({className, type, ...props}: React.ComponentProps<'input'>) {
className={merge(
`transition-[color] duration-200 ease-in-out`,
`h-10 min-w-0 w-full`,
- 'placeholder:text-muted-foreground',
+ 'placeholder:text-weak',
'selection:bg-primary selection:text-primary-foreground',
'flex rounded-md border bg-card px-3 py-1 text-base',
'outline-none focus-visible:ring-4 ring-ring/50',
diff --git a/src/middleware.ts b/src/middleware.ts
index 9129997..2a5d9d4 100644
--- a/src/middleware.ts
+++ b/src/middleware.ts
@@ -30,6 +30,8 @@ export async function middleware(request: NextRequest) {
}
}
catch (error) {
+ console.log(error)
+ console.log("redirect!!!!!!!!!")
return NextResponse.redirect(`${request.nextUrl.origin}/login?redirect=${request.nextUrl.pathname}`)
}
}