dark mode of overview card view
This commit is contained in:
parent
297b5280f0
commit
eaf1177cd4
@ -27,8 +27,8 @@ const APIKeyInfoPanel: FC = () => {
|
||||
return null
|
||||
|
||||
return (
|
||||
<div className={cn('bg-[#EFF4FF] border-[#D1E0FF]', 'mb-6 relative rounded-2xl shadow-md border p-8 ')}>
|
||||
<div className={cn('text-[24px] text-gray-800 font-semibold', isCloud ? 'flex items-center h-8 space-x-1' : 'leading-8 mb-6')}>
|
||||
<div className={cn('bg-components-panel-bg border-components-panel-border', 'mb-6 relative rounded-2xl shadow-md border p-8 ')}>
|
||||
<div className={cn('text-[24px] text-text-primary font-semibold', isCloud ? 'flex items-center h-8 space-x-1' : 'leading-8 mb-6')}>
|
||||
{isCloud && <em-emoji id={'😀'} />}
|
||||
{isCloud
|
||||
? (
|
||||
@ -42,11 +42,11 @@ const APIKeyInfoPanel: FC = () => {
|
||||
)}
|
||||
</div>
|
||||
{isCloud && (
|
||||
<div className='mt-1 text-sm text-gray-600 font-normal'>{t(`appOverview.apiKeyInfo.cloud.${'trial'}.description`)}</div>
|
||||
<div className='mt-1 text-sm text-text-tertiary font-normal'>{t(`appOverview.apiKeyInfo.cloud.${'trial'}.description`)}</div>
|
||||
)}
|
||||
<Button
|
||||
variant='primary'
|
||||
className='space-x-2'
|
||||
className='mt-2 space-x-2'
|
||||
onClick={() => setShowAccountSettingModal({ payload: 'provider' })}
|
||||
>
|
||||
<div className='text-sm font-medium'>{t('appOverview.apiKeyInfo.setAPIBtn')}</div>
|
||||
@ -65,7 +65,7 @@ const APIKeyInfoPanel: FC = () => {
|
||||
<div
|
||||
onClick={() => setIsShow(false)}
|
||||
className='absolute right-4 top-4 flex items-center justify-center w-8 h-8 cursor-pointer '>
|
||||
<RiCloseLine className='w-4 h-4 text-gray-500' />
|
||||
<RiCloseLine className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@ -1,29 +0,0 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import s from './style.module.css'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
export type IProgressProps = {
|
||||
className?: string
|
||||
value: number // percent
|
||||
}
|
||||
|
||||
const Progress: FC<IProgressProps> = ({
|
||||
className,
|
||||
value,
|
||||
}) => {
|
||||
const exhausted = value === 100
|
||||
return (
|
||||
<div className={cn(className, 'relative grow h-2 flex bg-gray-200 rounded-md overflow-hidden')}>
|
||||
<div
|
||||
className={cn(s.bar, exhausted && s['bar-error'], 'absolute top-0 left-0 right-0 bottom-0')}
|
||||
style={{ width: `${value}%` }}
|
||||
/>
|
||||
{Array.from({ length: 10 }).fill(0).map((i, k) => (
|
||||
<div key={k} className={s['bar-item']} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(Progress)
|
@ -1,16 +0,0 @@
|
||||
.bar {
|
||||
background: linear-gradient(90deg, rgba(41, 112, 255, 0.9) 0%, rgba(21, 94, 239, 0.9) 100%);
|
||||
}
|
||||
|
||||
.bar-error {
|
||||
background: linear-gradient(90deg, rgba(240, 68, 56, 0.72) 0%, rgba(217, 45, 32, 0.9) 100%);
|
||||
}
|
||||
|
||||
.bar-item {
|
||||
width: 10%;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.bar-item:last-of-type {
|
||||
border-right: 0;
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
'use client'
|
||||
import type { HTMLProps } from 'react'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import {
|
||||
RiLoopLeftLine,
|
||||
} from '@remixicon/react'
|
||||
import {
|
||||
Cog8ToothIcon,
|
||||
DocumentTextIcon,
|
||||
@ -16,24 +19,25 @@ import style from './style.module.css'
|
||||
import type { ConfigParams } from './settings'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import AppBasic from '@/app/components/app-sidebar/basic'
|
||||
import { asyncRunSafe, randomString } from '@/utils'
|
||||
import { asyncRunSafe } from '@/utils'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Tag from '@/app/components/base/tag'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import CopyFeedback from '@/app/components/base/copy-feedback'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import ShareQRCode from '@/app/components/base/qrcode'
|
||||
import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-button'
|
||||
import type { AppDetailResponse } from '@/models/app'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import type { AppSSO } from '@/types/app'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
export type IAppCardProps = {
|
||||
className?: string
|
||||
appInfo: AppDetailResponse & Partial<AppSSO>
|
||||
cardType?: 'api' | 'webapp'
|
||||
customBgColor?: string
|
||||
onChangeStatus: (val: boolean) => Promise<void>
|
||||
onSaveSiteConfig?: (params: ConfigParams) => Promise<void>
|
||||
onGenerateCode?: () => Promise<void>
|
||||
@ -46,7 +50,6 @@ const EmbedIcon = ({ className = '' }: HTMLProps<HTMLDivElement>) => {
|
||||
function AppCard({
|
||||
appInfo,
|
||||
cardType = 'webapp',
|
||||
customBgColor,
|
||||
onChangeStatus,
|
||||
onSaveSiteConfig,
|
||||
onGenerateCode,
|
||||
@ -92,10 +95,6 @@ function AppCard({
|
||||
const appUrl = `${app_base_url}/${appMode}/${access_token}`
|
||||
const apiUrl = appInfo?.api_base_url
|
||||
|
||||
let bgColor = 'bg-primary-50 bg-opacity-40'
|
||||
if (cardType === 'api')
|
||||
bgColor = 'bg-purple-50'
|
||||
|
||||
const genClickFuncByName = (opName: string) => {
|
||||
switch (opName) {
|
||||
case t('appOverview.overview.appInfo.preview'):
|
||||
@ -133,11 +132,8 @@ function AppCard({
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
`shadow-xs border-[0.5px] rounded-lg border-gray-200 ${className ?? ''}`}
|
||||
>
|
||||
<div className={`px-6 py-5 ${customBgColor ?? bgColor} rounded-lg`}>
|
||||
<div className={cn('rounded-xl border-effects-highlight border-t border-l-[0.5px] bg-background-default', className)}>
|
||||
<div className={cn('px-6 py-5')}>
|
||||
<div className="mb-2.5 flex flex-row items-start justify-between">
|
||||
<AppBasic
|
||||
iconType={cardType}
|
||||
@ -161,23 +157,20 @@ function AppCard({
|
||||
</div>
|
||||
<div className="flex flex-col justify-center py-2">
|
||||
<div className="py-1">
|
||||
<div className="pb-1 text-xs text-gray-500">
|
||||
<div className="pb-1 text-xs text-text-tertiary">
|
||||
{isApp
|
||||
? t('appOverview.overview.appInfo.accessibleAddress')
|
||||
: t('appOverview.overview.apiInfo.accessibleAddress')}
|
||||
</div>
|
||||
<div className="w-full h-9 pl-2 pr-0.5 py-0.5 bg-black bg-opacity-2 rounded-lg border border-black border-opacity-5 justify-start items-center inline-flex">
|
||||
<div className="w-full h-9 px-2 py-0.5 bg-components-input-bg-normal rounded-lg justify-start items-center inline-flex">
|
||||
<div className="h-4 px-2 justify-start items-start gap-2 flex flex-1 min-w-0">
|
||||
<div className="text-gray-700 text-xs font-medium text-ellipsis overflow-hidden whitespace-nowrap">
|
||||
<div className="text-text-secondary system-xs-medium truncate">
|
||||
{isApp ? appUrl : apiUrl}
|
||||
</div>
|
||||
</div>
|
||||
<Divider type="vertical" className="!h-3.5 shrink-0 !mx-0.5" />
|
||||
{isApp && <ShareQRCode content={isApp ? appUrl : apiUrl} selectorId={randomString(8)} className={'hover:bg-gray-200'} />}
|
||||
<CopyFeedback
|
||||
content={isApp ? appUrl : apiUrl}
|
||||
className={'hover:bg-gray-200'}
|
||||
/>
|
||||
<Divider type="vertical" className="!h-3.5 shrink-0" />
|
||||
{isApp && <ShareQRCode content={isApp ? appUrl : apiUrl} />}
|
||||
<CopyFeedback content={isApp ? appUrl : apiUrl}/>
|
||||
{/* button copy link/ button regenerate */}
|
||||
{showConfirmDelete && (
|
||||
<Confirm
|
||||
@ -196,22 +189,16 @@ function AppCard({
|
||||
<Tooltip
|
||||
popupContent={t('appOverview.overview.appInfo.regenerate') || ''}
|
||||
>
|
||||
<div
|
||||
className="w-8 h-8 ml-0.5 cursor-pointer hover:bg-gray-200 rounded-lg"
|
||||
onClick={() => setShowConfirmDelete(true)}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
`w-full h-full ${style.refreshIcon} ${genLoading ? style.generateLogo : ''}`}
|
||||
></div>
|
||||
</div>
|
||||
<ActionButton onClick={() => setShowConfirmDelete(true)}>
|
||||
<RiLoopLeftLine className={cn('w-4 h-4', genLoading && 'animate-spin')} />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={'pt-2 flex flex-row items-center flex-wrap gap-y-2'}>
|
||||
{!isApp && <SecretKeyButton className='flex-shrink-0 !h-8 bg-white mr-2' textCls='!text-gray-700 font-medium' iconCls='stroke-[1.2px]' appId={appInfo.id} />}
|
||||
{!isApp && <SecretKeyButton className='shrink-0 !h-8 mr-2' textCls='!text-text-secondary font-medium' iconCls='stroke-[1.2px]' appId={appInfo.id} />}
|
||||
{OPERATIONS_MAP[cardType].map((op) => {
|
||||
const disabled
|
||||
= op.opName === t('appOverview.overview.appInfo.settings.entry')
|
||||
|
@ -1,10 +1,15 @@
|
||||
'use client'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiClipboardFill,
|
||||
RiClipboardLine,
|
||||
} from '@remixicon/react'
|
||||
import { debounce } from 'lodash-es'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import copyStyle from './style.module.css'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
|
||||
type Props = {
|
||||
content: string
|
||||
@ -13,7 +18,7 @@ type Props = {
|
||||
|
||||
const prefixEmbedded = 'appOverview.overview.appInfo.embedded'
|
||||
|
||||
const CopyFeedback = ({ content, className }: Props) => {
|
||||
const CopyFeedback = ({ content }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const [isCopied, setIsCopied] = useState<boolean>(false)
|
||||
|
||||
@ -34,19 +39,15 @@ const CopyFeedback = ({ content, className }: Props) => {
|
||||
: t(`${prefixEmbedded}.copy`)) || ''
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={`w-8 h-8 cursor-pointer hover:bg-gray-100 rounded-lg ${
|
||||
className ?? ''
|
||||
}`}
|
||||
>
|
||||
<ActionButton>
|
||||
<div
|
||||
onClick={onClickCopy}
|
||||
onMouseLeave={onMouseLeave}
|
||||
className={`w-full h-full ${copyStyle.copyIcon} ${
|
||||
isCopied ? copyStyle.copied : ''
|
||||
}`}
|
||||
></div>
|
||||
</div>
|
||||
>
|
||||
{isCopied && <RiClipboardFill className='w-4 h-4' />}
|
||||
{!isCopied && <RiClipboardLine className='w-4 h-4' />}
|
||||
</div>
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
@ -1,19 +1,20 @@
|
||||
'use client'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiQrCodeLine,
|
||||
} from '@remixicon/react'
|
||||
import { QRCodeCanvas as QRCode } from 'qrcode.react'
|
||||
import QrcodeStyle from './style.module.css'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
|
||||
interface Props {
|
||||
type Props = {
|
||||
content: string
|
||||
selectorId: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
const prefixEmbedded = 'appOverview.overview.appInfo.qrcode.title'
|
||||
|
||||
const ShareQRCode = ({ content, selectorId, className }: Props) => {
|
||||
const ShareQRCode = ({ content }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const [isShow, setIsShow] = useState<boolean>(false)
|
||||
const qrCodeRef = useRef<HTMLDivElement>(null)
|
||||
@ -53,22 +54,21 @@ const ShareQRCode = ({ content, selectorId, className }: Props) => {
|
||||
<Tooltip
|
||||
popupContent={t(`${prefixEmbedded}`) || ''}
|
||||
>
|
||||
<div
|
||||
className={`w-8 h-8 cursor-pointer rounded-lg ${className ?? ''}`}
|
||||
onClick={toggleQRCode}
|
||||
>
|
||||
<div className={`w-full h-full ${QrcodeStyle.QrcodeIcon} ${isShow ? QrcodeStyle.show : ''}`} />
|
||||
<div className='relative w-6 h-6' onClick={toggleQRCode}>
|
||||
<ActionButton>
|
||||
<RiQrCodeLine className='w-4 h-4' />
|
||||
</ActionButton>
|
||||
{isShow && (
|
||||
<div
|
||||
ref={qrCodeRef}
|
||||
className={QrcodeStyle.qrcodeform}
|
||||
className='absolute top-8 -right-8 z-10 w-[232px] flex flex-col items-center bg-components-panel-bg shadow-xs rounded-lg p-4'
|
||||
onClick={handlePanelClick}
|
||||
>
|
||||
<QRCode size={160} value={content} className={QrcodeStyle.qrcodeimage}/>
|
||||
<div className={QrcodeStyle.text}>
|
||||
<div className={`text-gray-500 ${QrcodeStyle.scan}`}>{t('appOverview.overview.appInfo.qrcode.scan')}</div>
|
||||
<div className={`text-gray-500 ${QrcodeStyle.scan}`}>·</div>
|
||||
<div className={QrcodeStyle.download} onClick={downloadQR}>{t('appOverview.overview.appInfo.qrcode.download')}</div>
|
||||
<QRCode size={160} value={content} className='mb-2'/>
|
||||
<div className='flex items-center system-xs-regular'>
|
||||
<div className='text-text-tertiary'>{t('appOverview.overview.appInfo.qrcode.scan')}</div>
|
||||
<div className='text-text-tertiary'>·</div>
|
||||
<div className='text-text-accent-secondary cursor-pointer' onClick={downloadQR}>{t('appOverview.overview.appInfo.qrcode.download')}</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
@ -1,62 +0,0 @@
|
||||
.QrcodeIcon {
|
||||
background-image: url(~@/app/components/develop/secret-key/assets/qrcode.svg);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.QrcodeIcon:hover {
|
||||
background-image: url(~@/app/components/develop/secret-key/assets/qrcode-hover.svg);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.QrcodeIcon.show {
|
||||
background-image: url(~@/app/components/develop/secret-key/assets/qrcode-hover.svg);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.qrcodeimage {
|
||||
position: relative;
|
||||
object-fit: cover;
|
||||
}
|
||||
.scan {
|
||||
margin: 0;
|
||||
line-height: 1rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.download {
|
||||
position: relative;
|
||||
color: #155eef;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
}
|
||||
.text {
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
white-space: nowrap;
|
||||
gap: 4px;
|
||||
}
|
||||
.qrcodeform {
|
||||
z-index: 50;
|
||||
border: 0.5px solid #eaecf0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 !important;
|
||||
margin-top: 4px !important;
|
||||
margin-left: -75px !important;
|
||||
width: fit-content;
|
||||
position: relative;
|
||||
border-radius: 8px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 12px 16px -4px rgba(16, 24, 40, 0.08),
|
||||
0 4px 6px -2px rgba(16, 24, 40, 0.03);
|
||||
overflow: hidden;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 15px;
|
||||
gap: 8px;
|
||||
}
|
@ -23,7 +23,7 @@ const SecretKeyButton = ({ className, appId, iconCls, textCls }: ISecretKeyButto
|
||||
<path d="M9 3.66672C9.35362 3.66672 9.69276 3.80719 9.94281 4.05724C10.1929 4.30729 10.3333 4.64643 10.3333 5.00005M13 5.00005C13.0002 5.62483 12.854 6.24097 12.5732 6.79908C12.2924 7.3572 11.8847 7.84177 11.3829 8.21397C10.8811 8.58617 10.2991 8.83564 9.68347 8.94239C9.06788 9.04915 8.43584 9.01022 7.838 8.82872L6.33333 10.3334H5V11.6667H3.66667V13.0001H1.66667C1.48986 13.0001 1.32029 12.9298 1.19526 12.8048C1.07024 12.6798 1 12.5102 1 12.3334V10.6094C1.00004 10.4326 1.0703 10.263 1.19533 10.1381L5.17133 6.16205C5.00497 5.61206 4.95904 5.03268 5.0367 4.46335C5.11435 3.89402 5.31375 3.3481 5.62133 2.86275C5.92891 2.3774 6.33744 1.96401 6.81913 1.65073C7.30082 1.33745 7.84434 1.13162 8.41272 1.04725C8.9811 0.96289 9.56098 1.00197 10.1129 1.16184C10.6648 1.32171 11.1758 1.59861 11.6111 1.97369C12.0464 2.34878 12.3958 2.81324 12.6354 3.33548C12.8751 3.85771 12.9994 4.42545 13 5.00005Z" stroke="#1F2A37" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className={`text-[13px] text-gray-800 ${textCls}`}>{t('appApi.apiKey')}</div>
|
||||
<div className={`text-[13px] text-text-secondary ${textCls}`}>{t('appApi.apiKey')}</div>
|
||||
</Button>
|
||||
<SecretKeyModal isShow={isVisible} onClose={() => setVisible(false)} appId={appId} />
|
||||
</>
|
||||
|
@ -127,7 +127,7 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
|
||||
setCurrentWorkspace(currentWorkspaceResponse)
|
||||
}, [currentWorkspaceResponse])
|
||||
|
||||
const [theme, setTheme] = useState<Theme>(Theme.dark)
|
||||
const [theme, setTheme] = useState<Theme>(Theme.light)
|
||||
const handleSetTheme = useCallback((theme: Theme) => {
|
||||
setTheme(theme)
|
||||
globalThis.document.documentElement.setAttribute('data-theme', theme)
|
||||
|
Loading…
Reference in New Issue
Block a user