Next.js + Mantine mock SSO service providing account/password, phone verify-code, and Feishu QR login. Issues a one-time sso_code that the external app exchanges for user info. Includes: - Feishu callback fix: guard against duplicate/concurrent /api/login submissions of the same single-use authorization code (feishuLogin once-guard + memoized onLoginSuccess to stop effect re-fire). - ADAPTATION_GUIDE.md: how to switch from the in-memory code store to the real backend auth/index + inner_get_user_info endpoints. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
78 lines
1.7 KiB
TypeScript
78 lines
1.7 KiB
TypeScript
import { IconCheck, IconX } from '@tabler/icons-react'
|
|
import { notifications } from '@mantine/notifications'
|
|
|
|
const getNotificationWidth = (text: string) => {
|
|
const length = Math.max(1, text?.length || 0)
|
|
const width = Math.min(Math.max(length * 14 + 80, 180), 520)
|
|
return `${width}px`
|
|
}
|
|
|
|
const getSharedStyles = (text: string) => {
|
|
const width = getNotificationWidth(text)
|
|
const offset = `calc((100% - ${width}) / 2)`
|
|
|
|
return {
|
|
width,
|
|
styles: {
|
|
root: {
|
|
top: 20,
|
|
left: offset,
|
|
transform: 'translateX(-75%)',
|
|
width,
|
|
},
|
|
icon: {
|
|
width: '16px',
|
|
height: '16px',
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
export const showSuccessMsg = (text: string, config?: object) => {
|
|
const { width, styles } = getSharedStyles(text)
|
|
|
|
notifications.show({
|
|
message: text,
|
|
position: 'top-center',
|
|
withCloseButton: false,
|
|
color: '#52c41a',
|
|
autoClose: 5000,
|
|
icon: <IconCheck />,
|
|
styles,
|
|
...config,
|
|
style: {
|
|
width,
|
|
...(config && 'style' in config && typeof config.style === 'object'
|
|
? (config.style as object)
|
|
: {}),
|
|
},
|
|
})
|
|
}
|
|
|
|
export const showErrorMsg = (text: string, config?: object) => {
|
|
const { width, styles } = getSharedStyles(text)
|
|
|
|
notifications.show({
|
|
message: text,
|
|
position: 'top-center',
|
|
withCloseButton: false,
|
|
color: '#ff4d4f',
|
|
autoClose: 5000,
|
|
icon: <IconX size={14} />,
|
|
styles: {
|
|
...styles,
|
|
root: {
|
|
...styles.root,
|
|
transform: 'translateX(-75%)',
|
|
},
|
|
},
|
|
...config,
|
|
style: {
|
|
width,
|
|
...(config && 'style' in config && typeof config.style === 'object'
|
|
? (config.style as object)
|
|
: {}),
|
|
},
|
|
})
|
|
}
|