[Feature Request] Waffo Pancake gateway — full integration with subscription support + admin catalog binding flow (#4935)
This commit is contained in:
+80
-22
@@ -16,11 +16,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
For commercial licensing, please contact support@quantumnous.com
|
||||
*/
|
||||
import { useMemo } from 'react'
|
||||
import { Fragment, useMemo } from 'react'
|
||||
import { Link } from '@tanstack/react-router'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useSystemConfig } from '@/hooks/use-system-config'
|
||||
import { useStatus } from '@/hooks/use-status'
|
||||
|
||||
interface FooterLink {
|
||||
text: string
|
||||
@@ -74,23 +75,75 @@ function FooterLinkItem(props: { link: FooterLink }) {
|
||||
)
|
||||
}
|
||||
|
||||
function ProjectAttribution(props: { currentYear: number }) {
|
||||
// Renders User Agreement / Privacy Policy links inline with the parent's
|
||||
// copyright row when either is configured in System Settings → Site. Emits
|
||||
// fragmented siblings so the parent flex container's gap controls spacing.
|
||||
function LegalLinks(props: { leadingSeparator?: boolean }) {
|
||||
const { t } = useTranslation()
|
||||
const { status } = useStatus()
|
||||
const items: { key: string; label: string; href: string }[] = []
|
||||
if (status?.user_agreement_enabled) {
|
||||
items.push({
|
||||
key: 'user-agreement',
|
||||
label: t('User Agreement'),
|
||||
href: '/user-agreement',
|
||||
})
|
||||
}
|
||||
if (status?.privacy_policy_enabled) {
|
||||
items.push({
|
||||
key: 'privacy-policy',
|
||||
label: t('Privacy Policy'),
|
||||
href: '/privacy-policy',
|
||||
})
|
||||
}
|
||||
if (items.length === 0) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{items.map((item, index) => (
|
||||
<Fragment key={item.key}>
|
||||
{(props.leadingSeparator || index > 0) && (
|
||||
<span aria-hidden='true' className='text-muted-foreground/30'>
|
||||
·
|
||||
</span>
|
||||
)}
|
||||
<Link
|
||||
to={item.href}
|
||||
className='hover:text-foreground transition-colors duration-200'
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
</Fragment>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// inline=true returns just the inner span for composition in a parent flex
|
||||
// row. inline=false wraps in a centered/right-aligned div (default).
|
||||
function ProjectAttribution(props: { currentYear: number; inline?: boolean }) {
|
||||
const { t } = useTranslation()
|
||||
const content = (
|
||||
<span className='text-muted-foreground/45'>
|
||||
© {props.currentYear}{' '}
|
||||
<a
|
||||
href='https://github.com/QuantumNous/new-api'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='text-foreground/70 hover:text-foreground font-medium transition-colors'
|
||||
>
|
||||
{t('New API')}
|
||||
</a>
|
||||
. {t(NEW_API_FOOTER_ATTRIBUTION_KEY)}
|
||||
</span>
|
||||
)
|
||||
if (props.inline) {
|
||||
return content
|
||||
}
|
||||
return (
|
||||
<div className='text-muted-foreground/45 text-center text-xs sm:text-right'>
|
||||
<span className='text-muted-foreground/45'>
|
||||
© {props.currentYear}{' '}
|
||||
<a
|
||||
href='https://github.com/QuantumNous/new-api'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='text-foreground/70 hover:text-foreground font-medium transition-colors'
|
||||
>
|
||||
{t('New API')}
|
||||
</a>
|
||||
. {t(NEW_API_FOOTER_ATTRIBUTION_KEY)}
|
||||
</span>
|
||||
{content}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -182,8 +235,9 @@ export function Footer(props: FooterProps) {
|
||||
className='custom-footer text-muted-foreground min-w-0 text-center text-sm sm:text-left'
|
||||
dangerouslySetInnerHTML={{ __html: footerHtml }}
|
||||
/>
|
||||
<div className='border-border/60 w-full border-t pt-4 sm:w-auto sm:border-t-0 sm:border-l sm:pt-0 sm:pl-5'>
|
||||
<ProjectAttribution currentYear={currentYear} />
|
||||
<div className='border-border/60 flex w-full flex-wrap items-center justify-center gap-x-3 gap-y-1 border-t pt-4 text-muted-foreground/45 text-xs sm:w-auto sm:justify-end sm:border-t-0 sm:border-l sm:pt-0 sm:pl-5'>
|
||||
<LegalLinks />
|
||||
<ProjectAttribution currentYear={currentYear} inline />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -235,12 +289,16 @@ export function Footer(props: FooterProps) {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Bottom section */}
|
||||
<div className='border-border/30 mt-12 flex flex-col items-center justify-between gap-3 border-t pt-6 sm:flex-row'>
|
||||
<p className='text-muted-foreground/40 text-xs'>
|
||||
© {currentYear} {displayName}.{' '}
|
||||
{props.copyright ?? t('footer.defaultCopyright')}
|
||||
</p>
|
||||
{/* Copyright + optional legal links inline on the left, project
|
||||
attribution on the right; wraps on narrow screens. */}
|
||||
<div className='border-border/30 mt-12 flex flex-col items-center justify-between gap-x-3 gap-y-2 border-t pt-6 sm:flex-row'>
|
||||
<div className='text-muted-foreground/40 flex flex-wrap items-center justify-center gap-x-2 gap-y-1 text-xs sm:justify-start'>
|
||||
<span>
|
||||
© {currentYear} {displayName}.{' '}
|
||||
{props.copyright ?? t('footer.defaultCopyright')}
|
||||
</span>
|
||||
<LegalLinks leadingSeparator />
|
||||
</div>
|
||||
<ProjectAttribution currentYear={currentYear} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user