From 5d4d6de23ef55c83fb4856e4b7991ccff704d4a6 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Yadav Date: Fri, 3 Jul 2026 00:21:55 +0530 Subject: [PATCH 1/7] Auth Page Accessibility to Team Member --- .../src/controllers/project.controller.js | 14 +- apps/web-dashboard/src/pages/Auth.jsx | 46 +++- .../urbackend-react/src/components/UrAuth.tsx | 237 +++++++----------- 3 files changed, 135 insertions(+), 162 deletions(-) diff --git a/apps/dashboard-api/src/controllers/project.controller.js b/apps/dashboard-api/src/controllers/project.controller.js index df7efc20a..92b5cc352 100644 --- a/apps/dashboard-api/src/controllers/project.controller.js +++ b/apps/dashboard-api/src/controllers/project.controller.js @@ -453,13 +453,17 @@ module.exports.getSingleProject = async (req, res) => { await setProjectById(req.params.projectId, projectObj); } - if (!getProjectRole(projectObj, req.user._id)) { - throw new AppError(403, "Access denied."); - } - res.json(sanitizeProjectResponse(projectObj)); } catch (err) { - res.status(500).json({ error: err.message }); + if (err instanceof AppError) { + return res.status(err.statusCode).json({ + success: false, + data: {}, + message: err.message, + }); + } + + res.status(500).json({ success: false, data: {}, message: err.message }); } }; diff --git a/apps/web-dashboard/src/pages/Auth.jsx b/apps/web-dashboard/src/pages/Auth.jsx index 1967fb662..51cdd23db 100644 --- a/apps/web-dashboard/src/pages/Auth.jsx +++ b/apps/web-dashboard/src/pages/Auth.jsx @@ -15,6 +15,22 @@ import AddRecordDrawer from '../components/AddRecordDrawer'; import { PUBLIC_API_URL } from '../config'; export default function Auth() { + const unwrapApiResponse = (payload) => { + if (!payload || typeof payload !== 'object') { + return { ok: true, data: payload, message: '' }; + } + + if (Object.prototype.hasOwnProperty.call(payload, 'success')) { + return { + ok: payload.success !== false, + data: payload.data ?? payload.project ?? payload, + message: payload.message || payload.error || '' + }; + } + + return { ok: true, data: payload, message: '' }; + }; + const normalizeUsersResponse = (payload) => { if (Array.isArray(payload)) return payload; if (Array.isArray(payload?.items)) return payload.items; @@ -87,25 +103,39 @@ export default function Auth() { const fetchData = async () => { try { const projRes = await api.get(`/api/projects/${projectId}`); + const projectResult = unwrapApiResponse(projRes.data); + + if (!projectResult.ok) { + throw new Error(projectResult.message || 'Failed to load auth details'); + } + if (isMounted) { - setProject(projRes.data); - if (projRes.data.authProviders) setAuthProviders(projRes.data.authProviders); - if (projRes.data.isAuthEnabled) { + setProject(projectResult.data); + if (projectResult.data?.authProviders) setAuthProviders(projectResult.data.authProviders); + if (projectResult.data?.isAuthEnabled) { const requestId = ++latestUsersRequestId.current; const usersRes = await api.get( `/api/projects/${projectId}/admin/users?page=${page}&limit=${limit}` ); + const usersResult = unwrapApiResponse(usersRes.data); + + if (!usersResult.ok) { + throw new Error(usersResult.message || 'Failed to load auth users'); + } + if (!isMounted || requestId !== latestUsersRequestId.current) return; - setUsers(normalizeUsersResponse(usersRes.data)); + setUsers(normalizeUsersResponse(usersResult.data)); setTotalRecords( - usersRes.data?.data?.total || - usersRes.data?.total || - normalizeUsersResponse(usersRes.data).length + usersResult.data?.data?.total || + usersResult.data?.total || + normalizeUsersResponse(usersResult.data).length ); } } - } catch { toast.error("Failed to load auth details"); } + } catch (err) { + toast.error(err?.response?.data?.message || err?.response?.data?.error || err.message || "Failed to load auth details"); + } finally { if (isMounted) setLoading(false); } }; fetchData(); diff --git a/sdks/urbackend-react/src/components/UrAuth.tsx b/sdks/urbackend-react/src/components/UrAuth.tsx index 5790838c3..ae80769a9 100644 --- a/sdks/urbackend-react/src/components/UrAuth.tsx +++ b/sdks/urbackend-react/src/components/UrAuth.tsx @@ -13,7 +13,6 @@ interface AuthColors { border: string; inputBackground: string; primary: string; - primaryColor?: string; primaryText: string; footerBackground: string; dividerText: string; @@ -22,12 +21,9 @@ interface AuthColors { interface AuthBranding { brandName?: string; - appName?: string; title?: string; subtitle?: string; - logo?: React.ReactNode | string; - logoUrl?: string; - primaryColor?: string; + logo?: React.ReactNode; } interface AuthLabels { @@ -57,30 +53,16 @@ interface AuthLabels { footerSignupPrompt: string; footerForgotPrompt: string; noAuthMethods: string; - forgotSubtitle: string; - resetSubtitle: string; - // Aliases support - signInTitle?: string; - signUpTitle?: string; - signInTab?: string; - signUpTab?: string; - signInButton?: string; - signUpButton?: string; } export interface UrAuthProps { - providers?: AuthProvider[] | { - google?: boolean; - github?: boolean; - emailPassword?: boolean; - }; + providers?: AuthProvider[]; enableEmailPassword?: boolean; theme?: ThemeMode; colors?: Partial; branding?: AuthBranding; labels?: Partial; onSuccess?: () => void; - hideSignup?: boolean; } const defaultLabels: AuthLabels = { @@ -110,43 +92,37 @@ const defaultLabels: AuthLabels = { footerSignupPrompt: 'Already have an account?', footerForgotPrompt: 'Remember your password?', noAuthMethods: 'No authentication methods are enabled for this screen.', - forgotSubtitle: 'Welcome back', - resetSubtitle: 'Enter the code sent to {email}', }; const defaultThemeColors: Record = { light: { background: '#ffffff', surface: '#ffffff', - text: '#09090b', - textMuted: '#71717a', - border: '#e4e4e7', - inputBackground: '#fafafa', - primary: '#09090b', + text: '#0f172a', + textMuted: '#64748b', + border: '#e2e8f0', + inputBackground: '#ffffff', + primary: '#111111', primaryText: '#ffffff', - footerBackground: '#fafafa', - dividerText: '#a1a1aa', + footerBackground: '#f8fafc', + dividerText: '#94a3b8', socialButtonBackground: '#ffffff', }, dark: { - background: '#09090b', - surface: '#09090b', - text: '#fafafa', + background: '#1a1a1a', + surface: '#1a1a1a', + text: '#ffffff', textMuted: '#a1a1aa', - border: '#27272a', - inputBackground: '#09090b', - primary: '#fafafa', - primaryText: '#09090b', - footerBackground: '#18181b', - dividerText: '#52525b', - socialButtonBackground: '#09090b', + border: '#333333', + inputBackground: '#2a2a2a', + primary: '#ffffff', + primaryText: '#111111', + footerBackground: '#222222', + dividerText: '#94a3b8', + socialButtonBackground: '#2a2a2a', }, }; - - - - export const UrAuth: React.FC = ({ providers = ['google', 'github'], enableEmailPassword = true, @@ -154,53 +130,22 @@ export const UrAuth: React.FC = ({ colors, branding, labels, - onSuccess, - hideSignup = false + onSuccess }) => { const { login, signUp, socialLogin, requestPasswordReset, resetPassword, isLoading, error, clearError } = useAuth(); - const [mode, setMode] = useState<'signin' | 'signup' | 'forgot' | 'reset'>(hideSignup ? 'signin' : 'signin'); + const [mode, setMode] = useState<'signin' | 'signup' | 'forgot' | 'reset'>('signin'); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [otp, setOtp] = useState(''); const [name, setName] = useState(''); const [toast, setToast] = useState<{message: string, type: 'success' | 'error'} | null>(null); - const text = { - ...defaultLabels, - ...labels, - loginTab: labels?.signInTab ?? labels?.loginTab ?? defaultLabels.loginTab, - loginTitle: labels?.signInTitle ?? labels?.loginTitle ?? defaultLabels.loginTitle, - loginButton: labels?.signInButton ?? labels?.loginButton ?? defaultLabels.loginButton, - signupTab: labels?.signUpTab ?? labels?.signupTab ?? defaultLabels.signupTab, - signupTitle: labels?.signUpTitle ?? labels?.signupTitle ?? defaultLabels.signupTitle, - signupButton: labels?.signUpButton ?? labels?.signupButton ?? defaultLabels.signupButton, -}; - + const text = { ...defaultLabels, ...labels }; const themeColors = { ...defaultThemeColors[theme], ...colors }; - const primaryColor = branding?.primaryColor || colors?.primaryColor || themeColors.primary; - - let isGoogleEnabled = true; - let isGithubEnabled = true; - let isEmailPasswordEnabled = enableEmailPassword; - - if (providers) { - if (Array.isArray(providers)) { - isGoogleEnabled = providers.includes('google'); - isGithubEnabled = providers.includes('github'); - } else if (typeof providers === 'object') { - isGoogleEnabled = !!providers.google; - isGithubEnabled = !!providers.github; - isEmailPasswordEnabled = providers.emailPassword !== undefined ? providers.emailPassword : enableEmailPassword; - } - } - - const hasPasswordAuth = isEmailPasswordEnabled; - const hasSocialAuth = isGoogleEnabled || isGithubEnabled; - - const showSwitcher = hasPasswordAuth && !hideSignup; - const brandName = branding?.brandName || branding?.appName || branding?.title || 'urBackend'; + const hasPasswordAuth = enableEmailPassword; + const hasSocialAuth = providers.length > 0; + const brandName = branding?.brandName || branding?.title || 'urBackend'; const headerTitle = branding?.title || brandName; - const brandingLogo = branding?.logo ?? branding?.logoUrl; const headerSubtitle = branding?.subtitle || (mode === 'signin' ? text.loginTitle : mode === 'signup' @@ -208,6 +153,7 @@ export const UrAuth: React.FC = ({ : mode === 'forgot' ? text.forgotTitle : text.resetTitle); + const showSwitcher = hasPasswordAuth; useEffect(() => { if (error) { @@ -254,50 +200,48 @@ export const UrAuth: React.FC = ({ width: '100%', maxWidth: '420px', margin: '0 auto', - borderRadius: '16px', + borderRadius: '0', background: themeColors.background, - boxShadow: theme === 'dark' ? '0 0 0 1px rgba(255,255,255,0.08), 0 8px 32px rgba(0,0,0,0.3)' : '0 1px 3px rgba(0,0,0,0.05), 0 20px 40px -12px rgba(0,0,0,0.1)', + boxShadow: theme === 'dark' ? '0 20px 40px rgba(0,0,0,0.5)' : '0 20px 40px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.05)', border: `1px solid ${themeColors.border}`, overflow: 'hidden', - fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif', + fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', color: themeColors.text, }, body: { - padding: '40px 32px 32px 32px', + padding: '32px 32px 24px 32px', }, header: { textAlign: 'center' as const, - marginBottom: '32px', + marginBottom: '28px', }, brandRow: { display: 'flex', justifyContent: 'center', alignItems: 'center', gap: '12px', - marginBottom: '16px', + marginBottom: '10px', }, brandLogo: { - width: '48px', - height: '48px', + width: '44px', + height: '44px', borderRadius: '12px', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', - background: theme === 'dark' ? '#18181b' : '#f4f4f5', + background: theme === 'dark' ? '#2a2a2a' : '#f1f5f9', color: themeColors.text, overflow: 'hidden' as const, - border: `1px solid ${themeColors.border}`, }, brandTitle: { margin: 0, - fontSize: '24px', - lineHeight: 1.2, - fontWeight: 700, + fontSize: '26px', + lineHeight: 1.1, + fontWeight: 800, color: themeColors.text, - letterSpacing: '-0.02em', }, brandSubtitle: { - margin: '8px auto 0', + margin: '0 auto', maxWidth: '320px', fontSize: '14px', lineHeight: 1.5, @@ -310,28 +254,25 @@ export const UrAuth: React.FC = ({ marginBottom: '32px' }, switcher: { - display: 'flex', - background: theme === 'dark' ? '#18181b' : '#f4f4f5', + display: 'inline-flex', + background: theme === 'dark' ? '#2a2a2a' : '#f1f5f9', padding: '4px', - borderRadius: '10px', - border: `1px solid ${theme === 'dark' ? '#27272a' : '#e4e4e7'}`, - width: '100%', + borderRadius: '0', }, switchBtn: (active: boolean) => ({ display: 'flex', alignItems: 'center', - justifyContent: 'center', - flex: 1, - gap: '8px', - padding: '8px 0', - borderRadius: '6px', + gap: '6px', + padding: '8px 20px', + borderRadius: '0', fontSize: '13px', - fontWeight: 500, + fontWeight: 600, cursor: 'pointer', color: active ? themeColors.text : themeColors.textMuted, - background: active ? (theme === 'dark' ? '#27272a' : '#ffffff') : 'transparent', - boxShadow: active ? (theme === 'dark' ? '0 1px 2px rgba(0,0,0,0.2)' : '0 1px 2px rgba(0,0,0,0.05), 0 0 0 1px rgba(0,0,0,0.02)') : 'none', + background: active ? (theme === 'dark' ? '#444444' : '#ffffff') : 'transparent', + boxShadow: active ? (theme === 'dark' ? '0 2px 4px rgba(0,0,0,0.2)' : '0 2px 8px rgba(0,0,0,0.05)') : 'none', border: 'none', + transition: 'all 0.2s ease', }), field: { marginBottom: '20px', @@ -344,13 +285,13 @@ export const UrAuth: React.FC = ({ }, label: { fontSize: '13px', - fontWeight: 500, - color: themeColors.text, + fontWeight: 600, + color: theme === 'dark' ? '#dddddd' : '#334155', }, forgotLink: { - fontSize: '13px', - fontWeight: 500, - color: themeColors.textMuted, + fontSize: '12px', + fontWeight: 600, + color: themeColors.text, cursor: 'pointer', textDecoration: 'none', background: 'none', @@ -359,38 +300,38 @@ export const UrAuth: React.FC = ({ }, input: { width: '100%', - padding: '10px 12px', - borderRadius: '8px', + padding: '12px 16px', + borderRadius: '0', border: `1px solid ${themeColors.border}`, background: themeColors.inputBackground, color: themeColors.text, fontSize: '14px', boxSizing: 'border-box' as const, outline: 'none', - boxShadow: '0 1px 2px rgba(0,0,0,0.01)', + transition: 'border-color 0.2s ease', }, primaryBtn: { width: '100%', - padding: '10px 14px', - borderRadius: '8px', - background: primaryColor, + padding: '14px', + borderRadius: '0', + background: `linear-gradient(180deg, ${themeColors.primary} 0%, ${theme === 'dark' ? '#111111' : '#111111'} 100%)`, color: themeColors.primaryText, - fontSize: '14px', - fontWeight: 500, + fontSize: '15px', + fontWeight: 600, border: 'none', - boxShadow: theme === 'dark' ? 'inset 0 1px 0 rgba(255,255,255,0.1)' : '0 1px 2px rgba(0,0,0,0.05)', + boxShadow: '0 4px 12px rgba(0,0,0,0.15)', cursor: 'pointer', - marginTop: '12px', + marginTop: '8px', + transition: 'transform 0.1s ease', }, divider: { display: 'flex', alignItems: 'center', margin: '24px 0', color: themeColors.dividerText, - fontSize: '12px', - fontWeight: 400, - textTransform: 'uppercase' as const, - letterSpacing: '0.05em', + fontSize: '11px', + fontWeight: 600, + letterSpacing: '1px', }, dividerLine: { flex: 1, @@ -402,20 +343,21 @@ export const UrAuth: React.FC = ({ }, socialBtn: { width: '100%', - padding: '10px', - borderRadius: '8px', + padding: '12px', + borderRadius: '0', border: `1px solid ${themeColors.border}`, background: themeColors.socialButtonBackground, color: themeColors.text, fontSize: '14px', - fontWeight: 500, + fontWeight: 600, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '10px', marginBottom: '12px', cursor: 'pointer', - boxShadow: '0 1px 2px rgba(0,0,0,0.02)', + boxShadow: theme === 'dark' ? 'none' : '0 1px 2px rgba(0,0,0,0.02)', + transition: 'background 0.2s ease', }, footer: { background: themeColors.footerBackground, @@ -427,10 +369,10 @@ export const UrAuth: React.FC = ({ }, footerLink: { color: themeColors.text, - fontWeight: 500, - textDecoration: 'none', + fontWeight: 600, + textDecoration: 'underline', cursor: 'pointer', - marginLeft: '6px', + marginLeft: '4px', background: 'none', border: 'none', padding: 0, @@ -468,13 +410,13 @@ export const UrAuth: React.FC = ({ )}
- {isGoogleEnabled && ( + {providers.includes('google') && ( )} - {isGithubEnabled && ( + {providers.includes('github') && (
- {hasPasswordAuth && (!hideSignup || mode !== 'signin') && ( + {hasPasswordAuth && (
{footerPrompt} )} - {providers.includes('github') && ( + {isGithubEnabled && (
- {hasPasswordAuth && ( + {hasPasswordAuth && (!hideSignup || mode !== 'signin') && (
{footerPrompt}