feat: 修改样式
This commit is contained in:
parent
130a2506ac
commit
62880768fa
@ -77,7 +77,7 @@ export default function Content() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/*const { data } = useQuery({
|
const { data } = useQuery({
|
||||||
queryKey: ['getStat'],
|
queryKey: ['getStat'],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const { data } = await getStat({
|
const { data } = await getStat({
|
||||||
@ -86,13 +86,7 @@ export default function Content() {
|
|||||||
return data.data;
|
return data.data;
|
||||||
},
|
},
|
||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
});*/
|
});
|
||||||
const data = {
|
|
||||||
user: 1,
|
|
||||||
node: 2,
|
|
||||||
country: 0,
|
|
||||||
protocol: ['vmess', 'vless'],
|
|
||||||
};
|
|
||||||
|
|
||||||
const { data: announcementData } = useQuery({
|
const { data: announcementData } = useQuery({
|
||||||
queryKey: ['queryAnnouncement'],
|
queryKey: ['queryAnnouncement'],
|
||||||
@ -142,9 +136,11 @@ export default function Content() {
|
|||||||
{/* 账户概况 Card */}
|
{/* 账户概况 Card */}
|
||||||
<Card className='rounded-[20px] border border-[#D9D9D9] p-6 shadow-[0px_0px_52.6px_1px_rgba(15,44,83,0.05)]'>
|
<Card className='rounded-[20px] border border-[#D9D9D9] p-6 shadow-[0px_0px_52.6px_1px_rgba(15,44,83,0.05)]'>
|
||||||
<div className='mb-1 sm:mb-4'>
|
<div className='mb-1 sm:mb-4'>
|
||||||
<h3 className='text-base font-medium text-[#666666] sm:text-xl'>
|
{userSubscribe?.[0]?.status === 1 ? (
|
||||||
{t('accountOverview')}
|
<h3 className='text-base font-medium text-[#666666] sm:text-xl'>
|
||||||
</h3>
|
{t('accountOverview')}
|
||||||
|
</h3>
|
||||||
|
) : null}
|
||||||
<p className='mt-1 text-xs text-[#666666] sm:text-sm'>
|
<p className='mt-1 text-xs text-[#666666] sm:text-sm'>
|
||||||
{user?.auth_methods?.[0]?.auth_identifier}
|
{user?.auth_methods?.[0]?.auth_identifier}
|
||||||
</p>
|
</p>
|
||||||
@ -204,12 +200,15 @@ export default function Content() {
|
|||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<span className='text-xs sm:text-sm'>{t('availableDevices')}</span>
|
<span className='text-xs sm:text-sm'>{t('availableDevices')}</span>
|
||||||
<div className='flex gap-2'>
|
<div className='flex gap-2'>
|
||||||
<div className='h-4 w-4 rounded-full bg-[#225BA9]'></div>
|
{Array.from({ length: userSubscribe?.[0]?.subscribe.device_limit }).map(
|
||||||
<div className='h-4 w-4 rounded-full bg-[#D9D9D9]'></div>
|
(_, index) => {
|
||||||
<div className='h-4 w-4 rounded-full bg-[#D9D9D9]'></div>
|
return (
|
||||||
<div className='h-4 w-4 rounded-full bg-[#D9D9D9]'></div>
|
<div
|
||||||
<div className='h-4 w-4 rounded-full bg-[#D9D9D9]'></div>
|
className={`h-4 w-4 rounded-full ${index < (userSubscribe?.[0]?.subscribe.device_limit || 0) > 1 ? 'bg-[#225BA9]' : 'bg-[#D9D9D9]'}`}
|
||||||
<div className='h-4 w-4 rounded-full bg-[#D9D9D9]'></div>
|
></div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className='text-xs sm:text-sm'>
|
<span className='text-xs sm:text-sm'>
|
||||||
@ -314,7 +313,7 @@ export default function Content() {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{userSubscribe?.[0] && data.protocol ? (
|
{userSubscribe?.[0] && data?.protocol ? (
|
||||||
<div className='space-y-2 sm:space-y-4'>
|
<div className='space-y-2 sm:space-y-4'>
|
||||||
<p className='text-xs font-light text-[#666666] sm:text-sm sm:font-normal'>
|
<p className='text-xs font-light text-[#666666] sm:text-sm sm:font-normal'>
|
||||||
{t('copySubscriptionLinkOrScanQrCode')}
|
{t('copySubscriptionLinkOrScanQrCode')}
|
||||||
|
|||||||
@ -4,11 +4,13 @@ import { Display } from '@/components/display';
|
|||||||
import { Empty } from '@/components/empty';
|
import { Empty } from '@/components/empty';
|
||||||
import { ProList, ProListActions } from '@/components/pro-list';
|
import { ProList, ProListActions } from '@/components/pro-list';
|
||||||
import { closeOrder, queryOrderList } from '@/services/user/order';
|
import { closeOrder, queryOrderList } from '@/services/user/order';
|
||||||
import { Button } from '@workspace/airo-ui/components/button';
|
import { purchaseCheckout } from '@/services/user/portal';
|
||||||
|
import { AiroButton } from '@workspace/airo-ui/components/AiroButton';
|
||||||
import { Card, CardContent } from '@workspace/airo-ui/components/card';
|
import { Card, CardContent } from '@workspace/airo-ui/components/card';
|
||||||
import { formatDate } from '@workspace/airo-ui/utils';
|
import { formatDate } from '@workspace/airo-ui/utils';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
|
import { toast } from 'sonner';
|
||||||
import OrderDetailDialog from './components/OrderDetailDialog';
|
import OrderDetailDialog from './components/OrderDetailDialog';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
@ -16,6 +18,20 @@ export default function Page() {
|
|||||||
|
|
||||||
const ref = useRef<ProListActions>(null);
|
const ref = useRef<ProListActions>(null);
|
||||||
const OrderDetailDialogRef = useRef<typeof OrderDetailDialog>(null);
|
const OrderDetailDialogRef = useRef<typeof OrderDetailDialog>(null);
|
||||||
|
|
||||||
|
const handlePayment = (orderNo) => {
|
||||||
|
const data = await purchaseCheckout({
|
||||||
|
orderNo: orderNo,
|
||||||
|
returnUrl: window.location.href,
|
||||||
|
});
|
||||||
|
if (data.data?.type === 'url' && data.data.checkout_url) {
|
||||||
|
window.open(data.data.checkout_url, '_blank');
|
||||||
|
} else {
|
||||||
|
toast.success(t('paymentSuccess'));
|
||||||
|
ref?.current.reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ProList<API.OrderDetail, Record<string, unknown>>
|
<ProList<API.OrderDetail, Record<string, unknown>>
|
||||||
@ -38,26 +54,32 @@ export default function Page() {
|
|||||||
<div>
|
<div>
|
||||||
{item.status === 1 ? (
|
{item.status === 1 ? (
|
||||||
<>
|
<>
|
||||||
<Button
|
<AiroButton
|
||||||
className='min-w-[150px] rounded-full border-[#F8BFD2] bg-[#F8BFD2] px-[35px] py-[9px] text-center text-xl font-bold hover:border-[#F8BFD2] hover:bg-[#FF4248] hover:text-white'
|
variant={'danger'}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await closeOrder({ orderNo: item.order_no });
|
await closeOrder({ orderNo: item.order_no });
|
||||||
ref.current?.refresh();
|
ref.current?.refresh();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('cancelOrder')}
|
{t('cancelOrder')}
|
||||||
</Button>
|
</AiroButton>
|
||||||
<Button className='ml-3 min-w-[150px] rounded-full border-[#A8D4ED] bg-[#A8D4ED] px-[35px] py-[9px] text-center text-xl font-bold hover:border-[#225BA9] hover:bg-[#225BA9] hover:text-white'>
|
<AiroButton
|
||||||
|
variant={'primary'}
|
||||||
|
className={'ml-2'}
|
||||||
|
onClick={() => {
|
||||||
|
handlePayment(item.order_no);
|
||||||
|
}}
|
||||||
|
>
|
||||||
{t('payment')}
|
{t('payment')}
|
||||||
</Button>
|
</AiroButton>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<AiroButton
|
||||||
|
variant={'default'}
|
||||||
onClick={() => OrderDetailDialogRef?.current?.show(item.order_no)}
|
onClick={() => OrderDetailDialogRef?.current?.show(item.order_no)}
|
||||||
className='min-w-[150px] rounded-full border-[#0F2C53] bg-[#0F2C53] px-[35px] py-[9px] text-center text-xl font-bold hover:bg-[#225BA9] hover:text-white'
|
|
||||||
>
|
>
|
||||||
{t('detail')}
|
{t('detail')}
|
||||||
</Button>
|
</AiroButton>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
import LanguageSwitch from '@/components/language-switch';
|
||||||
import SvgIcon from '@/components/SvgIcon';
|
import SvgIcon from '@/components/SvgIcon';
|
||||||
import { UserNav } from '@/components/user-nav';
|
import { UserNav } from '@/components/user-nav';
|
||||||
import { navs } from '@/config/navs';
|
import { navs } from '@/config/navs';
|
||||||
@ -24,7 +25,7 @@ export function SidebarLeft({ ...props }: React.ComponentProps<typeof Sidebar>)
|
|||||||
<Sidebar side='left' {...props} className={'border-0 bg-transparent md:bg-white'}>
|
<Sidebar side='left' {...props} className={'border-0 bg-transparent md:bg-white'}>
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
'relative ml-2.5 flex h-[calc(100dvh-10px-env(safe-area-inset-top))] flex-col rounded-[30px] bg-[#D9D9D9] px-4 md:ml-0 md:h-full md:rounded-none md:bg-white'
|
'relative ml-2.5 flex h-[calc(100dvh-10px-env(safe-area-inset-top))] flex-col rounded-[30px] bg-[#D9D9D9] px-4 md:ml-0 md:h-full md:rounded-none md:bg-white md:px-8'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className={'absolute -right-3 top-6 md:hidden'} onClick={toggleSidebar}>
|
<div className={'absolute -right-3 top-6 md:hidden'} onClick={toggleSidebar}>
|
||||||
@ -35,7 +36,7 @@ export function SidebarLeft({ ...props }: React.ComponentProps<typeof Sidebar>)
|
|||||||
'absolute -left-2.5 top-2.5 -z-10 h-full w-full rounded-[30px] bg-[#225BA9] md:hidden'
|
'absolute -left-2.5 top-2.5 -z-10 h-full w-full rounded-[30px] bg-[#225BA9] md:hidden'
|
||||||
}
|
}
|
||||||
></div>
|
></div>
|
||||||
<div className='pb-7 pl-4 pt-5 md:pt-12'>
|
<div className='pb-7 pl-4 pt-5 md:pt-9'>
|
||||||
<Link href={'/dashboard'}>
|
<Link href={'/dashboard'}>
|
||||||
<Image
|
<Image
|
||||||
className={'cursor-pointer'}
|
className={'cursor-pointer'}
|
||||||
@ -59,14 +60,14 @@ export function SidebarLeft({ ...props }: React.ComponentProps<typeof Sidebar>)
|
|||||||
<SidebarMenuItem key={nav.title} className={''}>
|
<SidebarMenuItem key={nav.title} className={''}>
|
||||||
<SidebarMenuButton
|
<SidebarMenuButton
|
||||||
className={
|
className={
|
||||||
'h-[40px] rounded-full bg-[#EAEAEA] px-5 font-medium hover:bg-[#EAEAEA] hover:text-[#0F2C53] focus-visible:!outline-none focus-visible:!ring-0 active:bg-[#EAEAEA] active:text-[#0F2C53] data-[active=true]:bg-[#0F2C53] md:h-[60px] md:bg-white md:text-xl md:font-normal'
|
'h-[40px] rounded-full bg-[#EAEAEA] px-5 font-medium hover:bg-[#EAEAEA] hover:text-[#0F2C53] focus-visible:!outline-none focus-visible:!ring-0 active:bg-[#EAEAEA] active:text-[#0F2C53] data-[active=true]:bg-[#0F2C53] md:bg-white md:font-medium'
|
||||||
}
|
}
|
||||||
asChild
|
asChild
|
||||||
tooltip={t(nav.title)}
|
tooltip={t(nav.title)}
|
||||||
isActive={nav.url === pathname}
|
isActive={nav.url === pathname}
|
||||||
>
|
>
|
||||||
<Link href={nav.url} onClick={toggleSidebar} className={'gap-4'}>
|
<Link href={nav.url} onClick={toggleSidebar} className={'gap-4'}>
|
||||||
<div className={'relative size-4 md:!size-6'}>
|
<div className={'relative size-4'}>
|
||||||
<SvgIcon name={nav.image} />
|
<SvgIcon name={nav.image} />
|
||||||
</div>
|
</div>
|
||||||
<span>{t(nav.title)}</span>
|
<span>{t(nav.title)}</span>
|
||||||
@ -78,7 +79,10 @@ export function SidebarLeft({ ...props }: React.ComponentProps<typeof Sidebar>)
|
|||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
|
|
||||||
<SidebarFooter className={'mt-4 px-0'}>
|
<SidebarFooter className={'mb-6 mt-4 px-0 pb-0'}>
|
||||||
|
<div>
|
||||||
|
<LanguageSwitch />
|
||||||
|
</div>
|
||||||
<UserNav from='profile' />
|
<UserNav from='profile' />
|
||||||
</SidebarFooter>
|
</SidebarFooter>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -97,12 +97,6 @@ export default function Page() {
|
|||||||
<span className='absolute -top-8 left-16 z-10 rounded-md bg-[#E22C2E] px-2 py-0.5 text-[10px] font-bold leading-none text-white shadow sm:text-xs'>
|
<span className='absolute -top-8 left-16 z-10 rounded-md bg-[#E22C2E] px-2 py-0.5 text-[10px] font-bold leading-none text-white shadow sm:text-xs'>
|
||||||
-20%
|
-20%
|
||||||
{/* 小三角箭头 */}
|
{/* 小三角箭头 */}
|
||||||
{/* <span className="
|
|
||||||
absolute right-0 top-full
|
|
||||||
block h-0 w-0
|
|
||||||
border-l-[6px] border-r-[6px] border-t-[16px]
|
|
||||||
border-l-transparent border-r-transparent border-t-[#E22C2E]
|
|
||||||
" />*/}
|
|
||||||
<span
|
<span
|
||||||
className='absolute right-0 top-[80%] h-10 w-2 bg-[#E22C2E]'
|
className='absolute right-0 top-[80%] h-10 w-2 bg-[#E22C2E]'
|
||||||
style={{ clipPath: 'polygon(100% 0, 100% 100%, 0 0)' }}
|
style={{ clipPath: 'polygon(100% 0, 100% 100%, 0 0)' }}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import {
|
|||||||
updateUserTicketStatus,
|
updateUserTicketStatus,
|
||||||
} from '@/services/user/ticket';
|
} from '@/services/user/ticket';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { AiroButton } from '@workspace/airo-ui/components/AiroButton';
|
||||||
import { Button } from '@workspace/airo-ui/components/button';
|
import { Button } from '@workspace/airo-ui/components/button';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@ -89,13 +90,7 @@ export default function Page() {
|
|||||||
toolbar: (
|
toolbar: (
|
||||||
<Dialog open={create?.open} onOpenChange={(open) => setCreate({ open })}>
|
<Dialog open={create?.open} onOpenChange={(open) => setCreate({ open })}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button
|
<AiroButton className={'mr-3'}>{t('createTicket')}</AiroButton>
|
||||||
className={
|
|
||||||
'rounded-full border-[#0F2C53] bg-[#0F2C53] px-[35px] py-[9px] text-center font-bold hover:bg-[#225BA9] hover:text-white sm:min-w-[150px] sm:text-xl'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{t('createTicket')}
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className='sm:max-w-[425px]'>
|
<DialogContent className='sm:max-w-[425px]'>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
@ -183,23 +178,20 @@ export default function Page() {
|
|||||||
<CardDescription className='flex gap-2'>
|
<CardDescription className='flex gap-2'>
|
||||||
{item.status !== 4 ? (
|
{item.status !== 4 ? (
|
||||||
<>
|
<>
|
||||||
<Button
|
<AiroButton
|
||||||
key='reply'
|
variant={'primary'}
|
||||||
variant='destructive'
|
|
||||||
className={
|
|
||||||
'hidden min-w-[150px] rounded-full border-[#A8D4ED] bg-[#A8D4ED] text-center text-xl font-bold hover:border-[#225BA9] hover:bg-[#225BA9] hover:text-white sm:flex'
|
|
||||||
}
|
|
||||||
onClick={() => setTicketId(item.id)}
|
onClick={() => setTicketId(item.id)}
|
||||||
|
className={'hidden sm:flex'}
|
||||||
>
|
>
|
||||||
{t('reply')}
|
{t('reply')}
|
||||||
</Button>
|
</AiroButton>
|
||||||
<ConfirmButton
|
<ConfirmButton
|
||||||
key='close'
|
key='close'
|
||||||
trigger={
|
trigger={
|
||||||
<Button
|
<Button
|
||||||
variant='destructive'
|
variant='destructive'
|
||||||
className={
|
className={
|
||||||
'rounded-full border-white bg-transparent text-center font-bold text-[#FF4248] shadow-none hover:bg-transparent sm:min-w-[150px] sm:border-[#F8BFD2] sm:bg-[#F8BFD2] sm:text-xl sm:shadow sm:hover:border-[#F8BFD2] sm:hover:bg-[#FF4248] sm:hover:text-white'
|
'h-8 rounded-full border-white bg-transparent px-6 text-center text-sm font-bold text-[#FF4248] shadow-none hover:bg-transparent sm:min-w-[100px] sm:border-[#F8BFD2] sm:bg-[#F8BFD2] sm:text-white sm:shadow sm:hover:border-[#F8BFD2] sm:hover:bg-[#FF4248]'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{t('close')}
|
{t('close')}
|
||||||
@ -217,9 +209,13 @@ export default function Page() {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Button key='check' size='sm' onClick={() => setTicketId(item.id)}>
|
<AiroButton
|
||||||
|
key='check'
|
||||||
|
variant={'primary'}
|
||||||
|
onClick={() => setTicketId(item.id)}
|
||||||
|
>
|
||||||
{t('check')}
|
{t('check')}
|
||||||
</Button>
|
</AiroButton>
|
||||||
)}
|
)}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
@ -241,15 +237,9 @@ export default function Page() {
|
|||||||
</ul>
|
</ul>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter className={'flex justify-center sm:hidden'}>
|
<CardFooter className={'flex justify-center sm:hidden'}>
|
||||||
<Button
|
<AiroButton key='reply' variant={'primary'} onClick={() => setTicketId(item.id)}>
|
||||||
key='reply'
|
|
||||||
className={
|
|
||||||
'ml-3 min-w-[150px] rounded-full border-[#A8D4ED] bg-[#A8D4ED] px-[35px] py-[9px] text-center font-bold hover:border-[#225BA9] hover:bg-[#225BA9] hover:text-white sm:text-xl'
|
|
||||||
}
|
|
||||||
onClick={() => setTicketId(item.id)}
|
|
||||||
>
|
|
||||||
{t('reply')}
|
{t('reply')}
|
||||||
</Button>
|
</AiroButton>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -29,11 +29,7 @@ export default function Page() {
|
|||||||
<CardContent className='p-0'>
|
<CardContent className='p-0'>
|
||||||
<h2 className='mb-4 flex items-center justify-between text-base font-bold text-[#666] sm:text-xl'>
|
<h2 className='mb-4 flex items-center justify-between text-base font-bold text-[#666] sm:text-xl'>
|
||||||
<span>{t('assetOverview')}</span>
|
<span>{t('assetOverview')}</span>
|
||||||
<Recharge
|
<Recharge />
|
||||||
className={
|
|
||||||
'rounded-full border-[#A8D4ED] bg-[#A8D4ED] px-[35px] py-[9px] text-center text-base font-bold hover:bg-[#225BA9] hover:text-white sm:text-xl'
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</h2>
|
</h2>
|
||||||
<div className='mb-4'>
|
<div className='mb-4'>
|
||||||
<div className='flex items-center justify-between'>
|
<div className='flex items-center justify-between'>
|
||||||
|
|||||||
4
apps/user/assets/svg-icon/language.svg
Normal file
4
apps/user/assets/svg-icon/language.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="30" height="30" rx="15" fill="#EAEAEA"/>
|
||||||
|
<path d="M13.5879 15.7266H9.39258V17.9473H14.1738V19.5H7.62891V10.8633H13.9629V12.3926H9.39258V14.2266H13.5879V15.7266ZM22.5645 19.5H20.7598L17.2324 13.3652V19.5H15.5508V10.8633H17.4434L20.8828 16.8926V10.8633H22.5645V19.5Z" fill="#0F2C53"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 399 B |
@ -286,7 +286,7 @@ export default function Affiliate() {
|
|||||||
className='h-6 border-0 p-0 text-center text-sm focus-visible:ring-0 sm:h-8'
|
className='h-6 border-0 p-0 text-center text-sm focus-visible:ring-0 sm:h-8'
|
||||||
style={{ width: `${Math.max(3, String(count).length + 1)}ch` }}
|
style={{ width: `${Math.max(3, String(count).length + 1)}ch` }}
|
||||||
/>
|
/>
|
||||||
<span className='text-sm'>{t('users')}</span>
|
<span className='text-sm'>users</span>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
variant='secondary'
|
variant='secondary'
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import SvgIcon from '@/components/SvgIcon';
|
||||||
import { locales } from '@/config/constants';
|
import { locales } from '@/config/constants';
|
||||||
import { setLocale } from '@/utils/common';
|
import { setLocale } from '@/utils/common';
|
||||||
import {
|
import {
|
||||||
@ -52,10 +53,10 @@ export default function LanguageSwitch() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Select defaultValue={locale} onValueChange={handleLanguageChange}>
|
<Select defaultValue={locale} onValueChange={handleLanguageChange}>
|
||||||
<SelectTrigger className='hover:bg-accent hover:text-accent-foreground w-auto border-none bg-transparent p-2 shadow-none focus:ring-0 [&>svg]:hidden'>
|
<SelectTrigger className='hover:bg-accent hover:text-accent-foreground w-auto rounded-full border-none bg-transparent p-1 shadow-none focus:ring-0 [&>svg]:hidden'>
|
||||||
<SelectValue>
|
<SelectValue>
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center'>
|
||||||
<Icon icon={`flagpack:${country?.alpha2.toLowerCase()}`} className='!size-7' />
|
<SvgIcon name={'language'} />
|
||||||
<span className='sr-only'>{languages[locale as keyof typeof languages]}</span>
|
<span className='sr-only'>{languages[locale as keyof typeof languages]}</span>
|
||||||
</div>
|
</div>
|
||||||
</SelectValue>
|
</SelectValue>
|
||||||
|
|||||||
@ -123,7 +123,7 @@ const StarRating = ({ rating, maxRating = 5 }: { rating: number; maxRating?: num
|
|||||||
const FeatureList = ({ plan }: { plan: ProcessedPlanData }) => {
|
const FeatureList = ({ plan }: { plan: ProcessedPlanData }) => {
|
||||||
const t = useTranslations('subscribe.detail');
|
const t = useTranslations('subscribe.detail');
|
||||||
const tOffer = useTranslations('components.offerDialog');
|
const tOffer = useTranslations('components.offerDialog');
|
||||||
const features = [{ label: tOffer('availableNodes'), value: plan.features?.nodes || '11' }];
|
const features = [{ label: tOffer('availableNodes'), value: plan?.server_count }];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-6 space-y-0 sm:mt-6'>
|
<div className='mt-6 space-y-0 sm:mt-6'>
|
||||||
@ -168,7 +168,7 @@ const FeatureList = ({ plan }: { plan: ProcessedPlanData }) => {
|
|||||||
<span className='text-xs font-light leading-[1.8461538461538463em] text-black sm:text-[13px]'>
|
<span className='text-xs font-light leading-[1.8461538461538463em] text-black sm:text-[13px]'>
|
||||||
{tOffer('networkStabilityIndex')}
|
{tOffer('networkStabilityIndex')}
|
||||||
</span>
|
</span>
|
||||||
<StarRating rating={plan.features?.stability || 4} />
|
<StarRating rating={5} />
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
/*
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@workspace/ui/components/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@workspace/ui/components/card';
|
||||||
@ -47,3 +48,85 @@ export function SubscribeBilling({ order }: { order: API.Order }) {
|
|||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { Display } from '@/components/display';
|
||||||
|
import { Separator } from '@workspace/airo-ui/components/separator';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
|
||||||
|
interface SubscribeBillingProps {
|
||||||
|
order?: Partial<
|
||||||
|
API.OrderDetail & {
|
||||||
|
unit_price: number;
|
||||||
|
unit_time: number;
|
||||||
|
subscribe_discount: number;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SubscribeBilling({ order }: Readonly<SubscribeBillingProps>) {
|
||||||
|
const t = useTranslations('subscribe');
|
||||||
|
const t_c = useTranslations('components.billing');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='mb-1 font-semibold text-[#225BA9]'>{t('billing.billingTitle')}</div>
|
||||||
|
<ul className='grid grid-cols-1 gap-1 text-[15px] font-light text-[#666] *:flex *:items-center *:justify-between lg:grid-cols-1'>
|
||||||
|
<li>
|
||||||
|
<span className=''>{t('billing.duration')}</span>
|
||||||
|
<span>
|
||||||
|
{order?.quantity === 1 ? t_c('30days') : ''}
|
||||||
|
{order?.quantity === 12 ? t_c('365days') : ''}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
{order?.type && [1, 2].includes(order?.type) && (
|
||||||
|
<li>
|
||||||
|
<span className=''>{t('billing.duration')}</span>
|
||||||
|
<span>
|
||||||
|
{order?.quantity || 1} {t(order?.unit_time || 'Month')}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
<li>
|
||||||
|
<span className=''>{t('billing.price')}</span>
|
||||||
|
<span>
|
||||||
|
<Display type='currency' value={order?.price || order?.unit_price} />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className=''>{t('billing.productDiscount')}</span>
|
||||||
|
<span>
|
||||||
|
<Display type='currency' value={order?.discount} />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className=''>{t('billing.couponDiscount')}</span>
|
||||||
|
<span>
|
||||||
|
<Display type='currency' value={order?.coupon_discount} />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className='text-muted-foreground'>{t('billing.fee')}</span>
|
||||||
|
<span>
|
||||||
|
<Display type='currency' value={order?.fee_amount} />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span className='text-muted-foreground'>{t('billing.gift')}</span>
|
||||||
|
<span>
|
||||||
|
<Display type='currency' value={order?.gift_amount} />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<Separator className={'mb-3 mt-4 bg-[#225BA9]'} />
|
||||||
|
<div className='flex items-center justify-between font-semibold text-[#666]'>
|
||||||
|
<span className=''>{t('billing.total')}</span>
|
||||||
|
<span>
|
||||||
|
<Display type='currency' value={order?.amount} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import useGlobalStore from '@/config/use-global';
|
import useGlobalStore from '@/config/use-global';
|
||||||
import { preCreateOrder, purchase } from '@/services/user/order';
|
import { preCreateOrder, purchase } from '@/services/user/order';
|
||||||
|
import { purchaseCheckout } from '@/services/user/portal';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { Button } from '@workspace/airo-ui/components/button';
|
import { Button } from '@workspace/airo-ui/components/button';
|
||||||
import {
|
import {
|
||||||
@ -23,6 +24,7 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
useTransition,
|
useTransition,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
import { toast } from 'sonner';
|
||||||
import { SubscribeBilling } from './billing';
|
import { SubscribeBilling } from './billing';
|
||||||
import { SubscribeDetail } from './detail';
|
import { SubscribeDetail } from './detail';
|
||||||
|
|
||||||
@ -74,6 +76,7 @@ const Purchase = forwardRef<PurchaseDialogRef, PurchaseProps>((props, ref) => {
|
|||||||
queryKey: ['preCreateOrder', subscribe?.id, params.quantity],
|
queryKey: ['preCreateOrder', subscribe?.id, params.quantity],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
try {
|
try {
|
||||||
|
console.log('123123', subscribe);
|
||||||
const { data } = await preCreateOrder({
|
const { data } = await preCreateOrder({
|
||||||
...params,
|
...params,
|
||||||
subscribe_id: subscribe?.id as number,
|
subscribe_id: subscribe?.id as number,
|
||||||
@ -107,7 +110,16 @@ const Purchase = forwardRef<PurchaseDialogRef, PurchaseProps>((props, ref) => {
|
|||||||
const orderNo = response.data.data?.order_no;
|
const orderNo = response.data.data?.order_no;
|
||||||
if (orderNo) {
|
if (orderNo) {
|
||||||
await getUserInfo();
|
await getUserInfo();
|
||||||
router.push(`/dashboard`);
|
const data = await purchaseCheckout({
|
||||||
|
orderNo: orderNo,
|
||||||
|
returnUrl: window.location.href,
|
||||||
|
});
|
||||||
|
if (data.data?.type === 'url' && data.data.checkout_url) {
|
||||||
|
window.open(data.data.checkout_url, '_blank');
|
||||||
|
} else {
|
||||||
|
toast.success(t('paymentSuccess'));
|
||||||
|
router.push(`/dashboard`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
/* empty */
|
/* empty */
|
||||||
@ -144,12 +156,6 @@ const Purchase = forwardRef<PurchaseDialogRef, PurchaseProps>((props, ref) => {
|
|||||||
<span className='absolute -top-8 left-16 z-10 rounded-md bg-[#E22C2E] px-2 py-0.5 text-[10px] font-bold leading-none text-white shadow sm:text-xs'>
|
<span className='absolute -top-8 left-16 z-10 rounded-md bg-[#E22C2E] px-2 py-0.5 text-[10px] font-bold leading-none text-white shadow sm:text-xs'>
|
||||||
{t('discount20')}
|
{t('discount20')}
|
||||||
{/* 小三角箭头 */}
|
{/* 小三角箭头 */}
|
||||||
{/* <span className="
|
|
||||||
absolute right-0 top-full
|
|
||||||
block h-0 w-0
|
|
||||||
border-l-[6px] border-r-[6px] border-t-[16px]
|
|
||||||
border-l-transparent border-r-transparent border-t-[#E22C2E]
|
|
||||||
" />*/}
|
|
||||||
<span
|
<span
|
||||||
className='absolute right-0 top-[80%] h-10 w-2 bg-[#E22C2E]'
|
className='absolute right-0 top-[80%] h-10 w-2 bg-[#E22C2E]'
|
||||||
style={{ clipPath: 'polygon(100% 0, 100% 100%, 0 0)' }}
|
style={{ clipPath: 'polygon(100% 0, 100% 100%, 0 0)' }}
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import useGlobalStore from '@/config/use-global';
|
import useGlobalStore from '@/config/use-global';
|
||||||
import { recharge } from '@/services/user/order';
|
import { recharge } from '@/services/user/order';
|
||||||
|
import { AiroButton } from '@workspace/airo-ui/components/AiroButton';
|
||||||
import { Button, ButtonProps } from '@workspace/airo-ui/components/button';
|
import { Button, ButtonProps } from '@workspace/airo-ui/components/button';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@ -36,7 +37,9 @@ export default function Recharge(props: Readonly<ButtonProps>) {
|
|||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={setOpen}>
|
<Dialog open={open} onOpenChange={setOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button {...props}>{t('walletRecharge')}</Button>
|
<AiroButton variant={'primary'} {...props}>
|
||||||
|
{t('walletRecharge')}
|
||||||
|
</AiroButton>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className='flex h-full flex-col overflow-hidden md:h-auto'>
|
<DialogContent className='flex h-full flex-col overflow-hidden md:h-auto'>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
|
|||||||
@ -28,18 +28,18 @@ export function UserNav({ from = '' }: { from?: string }) {
|
|||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
{from === 'profile' ? (
|
{from === 'profile' ? (
|
||||||
<div className='mb-3 flex cursor-pointer items-center gap-2 rounded-full bg-[#EAEAEA] p-1 pr-6'>
|
<div className='mb-3 mt-4 flex cursor-pointer items-center gap-2 rounded-full bg-[#EAEAEA] p-[3px] pr-6'>
|
||||||
<Avatar className='h-[34px] w-[34px] md:h-[52px] md:w-[52px]'>
|
<Avatar className='h-[34px] w-[34px]'>
|
||||||
<AvatarImage
|
<AvatarImage
|
||||||
alt={user?.avatar ?? ''}
|
alt={user?.avatar ?? ''}
|
||||||
src={user?.avatar ?? ''}
|
src={user?.avatar ?? ''}
|
||||||
className='object-cover'
|
className='object-cover'
|
||||||
/>
|
/>
|
||||||
<AvatarFallback className='to-primary text-background bg-[#0F2C53] bg-gradient-to-br text-[28px] font-bold md:text-[40px]'>
|
<AvatarFallback className='to-primary text-background bg-[#0F2C53] bg-gradient-to-br text-[28px] font-bold md:text-[27px]'>
|
||||||
{user?.auth_methods?.[0]?.auth_identifier.toUpperCase().charAt(0)}
|
{user?.auth_methods?.[0]?.auth_identifier.toUpperCase().charAt(0)}
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<div className='flex flex-1 items-center justify-between text-xs md:text-base'>
|
<div className='flex flex-1 items-center justify-between text-xs md:text-sm'>
|
||||||
{user?.auth_methods?.[0]?.auth_identifier.split('@')[0]}
|
{user?.auth_methods?.[0]?.auth_identifier.split('@')[0]}
|
||||||
</div>
|
</div>
|
||||||
<Icon icon='lucide:ellipsis' className='text-muted-foreground !size-6' />
|
<Icon icon='lucide:ellipsis' className='text-muted-foreground !size-6' />
|
||||||
@ -61,13 +61,13 @@ export function UserNav({ from = '' }: { from?: string }) {
|
|||||||
forceMount
|
forceMount
|
||||||
align='end'
|
align='end'
|
||||||
side={from === 'profile' ? (isMobile ? 'top' : 'right') : undefined}
|
side={from === 'profile' ? (isMobile ? 'top' : 'right') : undefined}
|
||||||
className='flex w-[var(--sidebar-width)] flex-col gap-1 rounded-[42px] border-0 bg-transparent pl-[26px] pr-4 shadow-none md:w-64 md:gap-3 md:border md:bg-white md:p-3 md:shadow-md'
|
className='mt-[1px] flex w-[var(--sidebar-width)] flex-col gap-1 rounded-[28px] border-0 bg-transparent pl-[26px] pr-4 shadow-none md:w-48 md:gap-3 md:border md:bg-white md:p-3'
|
||||||
style={{
|
style={{
|
||||||
'--sidebar-width': '14rem',
|
'--sidebar-width': '14rem',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className='flex items-center justify-start gap-2 rounded-full bg-white p-1 md:bg-[#EAEAEA]'>
|
<div className='flex items-center justify-start gap-2 rounded-full bg-white p-[3px] md:bg-[#EAEAEA]'>
|
||||||
<Avatar className='h-[34px] w-[34px] md:h-[52px] md:w-[52px]'>
|
<Avatar className='h-[34px] w-[34px]'>
|
||||||
<AvatarImage
|
<AvatarImage
|
||||||
alt={user?.avatar ?? ''}
|
alt={user?.avatar ?? ''}
|
||||||
src={user?.avatar ?? ''}
|
src={user?.avatar ?? ''}
|
||||||
@ -77,11 +77,11 @@ export function UserNav({ from = '' }: { from?: string }) {
|
|||||||
{user?.auth_methods?.[0]?.auth_identifier.toUpperCase().charAt(0)}
|
{user?.auth_methods?.[0]?.auth_identifier.toUpperCase().charAt(0)}
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<div className='flex flex-col space-y-0.5'>
|
<div className='flex flex-col'>
|
||||||
<p className='text-xs font-medium leading-none md:text-xl'>
|
<p className='text-xs font-medium leading-none md:text-sm'>
|
||||||
{user?.auth_methods?.[0]?.auth_identifier.split('@')[0]}
|
{user?.auth_methods?.[0]?.auth_identifier.split('@')[0]}
|
||||||
</p>
|
</p>
|
||||||
<p className='text-muted-foreground text-[10px] md:text-xs'>
|
<p className='text-muted-foreground text-[10px]'>
|
||||||
{user?.auth_methods?.[0]?.auth_identifier}
|
{user?.auth_methods?.[0]?.auth_identifier}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -101,9 +101,9 @@ export function UserNav({ from = '' }: { from?: string }) {
|
|||||||
toggleSidebar();
|
toggleSidebar();
|
||||||
router.push(`${item.url}`);
|
router.push(`${item.url}`);
|
||||||
}}
|
}}
|
||||||
className='flex cursor-pointer items-center gap-3 rounded-full bg-white px-5 py-2 text-base font-medium focus:bg-[#0F2C53] focus:text-white data-[active=true]:bg-[#0F2C53] data-[active=true]:text-white md:text-xl'
|
className='flex cursor-pointer items-center gap-3 rounded-full bg-white px-5 py-2.5 text-base font-medium focus:bg-[#0F2C53] focus:text-white data-[active=true]:bg-[#0F2C53] data-[active=true]:text-white md:text-sm'
|
||||||
>
|
>
|
||||||
<SvgIcon className='!size-4 flex-none md:!size-6' name={item.icon} />
|
<SvgIcon className='!size-4 flex-none' name={item.icon} />
|
||||||
<span className='flex-grow truncate'>{t(item.title)}</span>
|
<span className='flex-grow truncate'>{t(item.title)}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
))}
|
))}
|
||||||
@ -112,14 +112,9 @@ export function UserNav({ from = '' }: { from?: string }) {
|
|||||||
Logout();
|
Logout();
|
||||||
setUser();
|
setUser();
|
||||||
}}
|
}}
|
||||||
className='flex cursor-pointer items-center gap-3 rounded-full bg-[#E22C2E] px-5 py-2 text-base font-medium text-white focus:bg-[#E22C2E] focus:text-white md:bg-white md:text-xl md:text-[#0F2C53]'
|
className='flex cursor-pointer items-center gap-3 rounded-full bg-[#E22C2E] px-5 py-2 text-base font-medium text-white focus:bg-[#E22C2E] focus:text-white md:bg-white md:text-sm md:text-[#0F2C53]'
|
||||||
>
|
>
|
||||||
<SvgIcon
|
<SvgIcon className='!size-4 flex-none' width='99' height='100' name={'exit'} />
|
||||||
className='!size-4 flex-none md:!size-6'
|
|
||||||
width='99'
|
|
||||||
height='100'
|
|
||||||
name={'exit'}
|
|
||||||
/>
|
|
||||||
<span className='flex-grow'>{t('logout')}</span>
|
<span className='flex-grow'>{t('logout')}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
|
|||||||
@ -43,6 +43,7 @@
|
|||||||
},
|
},
|
||||||
"monthlyPlan": "Monthly Plan",
|
"monthlyPlan": "Monthly Plan",
|
||||||
"paymentMethod": "Payment Method",
|
"paymentMethod": "Payment Method",
|
||||||
|
"paymentSuccess": "Success",
|
||||||
"productDescription": "Product Description",
|
"productDescription": "Product Description",
|
||||||
"products": "Products",
|
"products": "Products",
|
||||||
"purchaseDuration": "Purchase Duration",
|
"purchaseDuration": "Purchase Duration",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"check": "Check",
|
"check": "Check",
|
||||||
"close": "Close",
|
"close": "Close Ticket",
|
||||||
"closeSuccess": "Closed successfully",
|
"closeSuccess": "Closed successfully",
|
||||||
"closeWarning": "Once closed, the ticket cannot be operated on. Please proceed with caution.",
|
"closeWarning": "Once closed, the ticket cannot be operated on. Please proceed with caution.",
|
||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
|
|||||||
@ -43,6 +43,7 @@
|
|||||||
},
|
},
|
||||||
"monthlyPlan": "月付套餐",
|
"monthlyPlan": "月付套餐",
|
||||||
"paymentMethod": "支付方式",
|
"paymentMethod": "支付方式",
|
||||||
|
"paymentSuccess": "订阅成功",
|
||||||
"productDescription": "商品描述",
|
"productDescription": "商品描述",
|
||||||
"products": "商品",
|
"products": "商品",
|
||||||
"purchaseDuration": "购买时长",
|
"purchaseDuration": "购买时长",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"cancel": "取消",
|
"cancel": "取消",
|
||||||
"check": "查看",
|
"check": "查看",
|
||||||
"close": "关闭",
|
"close": "关闭工单",
|
||||||
"closeSuccess": "工单关闭成功",
|
"closeSuccess": "工单关闭成功",
|
||||||
"closeWarning": "关闭工单后将无法继续回复。",
|
"closeWarning": "关闭工单后将无法继续回复。",
|
||||||
"confirm": "确认",
|
"confirm": "确认",
|
||||||
|
|||||||
49
packages/airo-ui/src/components/AiroButton.tsx
Normal file
49
packages/airo-ui/src/components/AiroButton.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { Slot } from '@radix-ui/react-slot';
|
||||||
|
import { cva, type VariantProps } from 'class-variance-authority';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { cn } from '@workspace/airo-ui/lib/utils';
|
||||||
|
|
||||||
|
const buttonVariants = cva(
|
||||||
|
'px-6 rounded-full inline-flex items-center justify-center gap-2 whitespace-nowrap 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-[#0F2C53] text-primary-foreground shadow hover:bg-[#225BA9]',
|
||||||
|
primary: 'bg-[#A8D4ED] text-primary-foreground shadow hover:bg-[#225BA9] ',
|
||||||
|
danger: 'bg-[#F8BFD2] text-primary-foreground shadow hover:bg-[#FF4248]',
|
||||||
|
destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
|
||||||
|
outline:
|
||||||
|
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
||||||
|
secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
|
||||||
|
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||||
|
link: 'text-primary underline-offset-4 hover:underline',
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: 'h-8 min-w-[100px]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: 'default',
|
||||||
|
size: 'default',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export interface ButtonProps
|
||||||
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
|
VariantProps<typeof buttonVariants> {
|
||||||
|
asChild?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AiroButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||||
|
const Comp = asChild ? Slot : 'button';
|
||||||
|
return (
|
||||||
|
<Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
AiroButton.displayName = 'AiroButton';
|
||||||
|
|
||||||
|
export { AiroButton, buttonVariants };
|
||||||
@ -148,7 +148,11 @@ export function ProList<TData, TValue extends Record<string, unknown>>({
|
|||||||
<div className='flex flex-1 items-center justify-end gap-2'>
|
<div className='flex flex-1 items-center justify-end gap-2'>
|
||||||
{params && params?.length > 0 && (
|
{params && params?.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Button variant='outline' className='h-8 w-8 bg-[#D9D9D9] p-2' onClick={fetchData}>
|
<Button
|
||||||
|
variant='outline'
|
||||||
|
className='h-8 w-8 rounded-full bg-[#D9D9D9] p-2'
|
||||||
|
onClick={fetchData}
|
||||||
|
>
|
||||||
<RefreshCcw className='h-4 w-4 text-[#848484]' />
|
<RefreshCcw className='h-4 w-4 text-[#848484]' />
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -7,8 +7,8 @@ const config = {
|
|||||||
content: [
|
content: [
|
||||||
'app/**/*.{ts,tsx}',
|
'app/**/*.{ts,tsx}',
|
||||||
'components/**/*.{ts,tsx}',
|
'components/**/*.{ts,tsx}',
|
||||||
'../../packages/ui/src/components/**/*.{ts,tsx}',
|
'../../packages/airo-ui/src/components/**/*.{ts,tsx}',
|
||||||
'../../packages/ui/src/custom-components/**/*.{ts,tsx}',
|
'../../packages/airo-ui/src/custom-components/**/*.{ts,tsx}',
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user