frontend changes

This commit is contained in:
Chandini 2025-09-17 17:13:22 +05:30
parent 906f763a78
commit 4983f7a2e6
10 changed files with 63 additions and 75 deletions

View File

@ -129,13 +129,19 @@ const addTokenRefreshInterceptor = (client: typeof authApiClient) => {
// No refresh token available
clearTokens();
safeLocalStorage.removeItem('codenuk_user');
window.location.href = '/signin';
// Prevent redirect loops by checking current location
if (typeof window !== 'undefined' && !window.location.pathname.includes('/signin')) {
window.location.href = '/signin?error=Please sign in to continue.';
}
return Promise.reject(error);
} catch (refreshError) {
console.error('Token refresh failed:', refreshError);
clearTokens();
safeLocalStorage.removeItem('codenuk_user');
window.location.href = '/signin';
// Prevent redirect loops by checking current location
if (typeof window !== 'undefined' && !window.location.pathname.includes('/signin')) {
window.location.href = '/signin?error=Session expired. Please sign in again.';
}
return Promise.reject(refreshError);
}
}

View File

@ -48,12 +48,18 @@ export function SignInForm({ }: SignInFormProps) {
// Persist for refresh
localStorage.setItem("codenuk_user", JSON.stringify(response.data.user))
// Redirect based on user role
// Redirect based on user role with error handling
try {
if (response.data.user.role === 'admin') {
router.push("/admin")
} else {
router.push("/")
}
} catch (redirectError) {
console.error('Redirect failed:', redirectError)
// Fallback redirect
window.location.href = response.data.user.role === 'admin' ? '/admin' : '/'
}
} else {
setError("Invalid response from server. Please try again.")
}

View File

@ -72,7 +72,14 @@ export function SignUpForm({ onSignUpSuccess }: SignUpFormProps) {
} else {
// Default behavior - redirect to signin with message
if (typeof window !== 'undefined') {
router.push("/signin?message=Account created successfully! Please check your email to verify your account.")
const message = encodeURIComponent("Account created successfully! Please check your email to verify your account.")
try {
router.push(`/signin?message=${message}`)
} catch (redirectError) {
console.error('Signup form redirect failed:', redirectError)
// Fallback redirect
window.location.href = `/signin?message=${message}`
}
}
}
} else {

View File

@ -12,12 +12,19 @@ export function SignUpPage() {
const handleSignUpSuccess = () => {
setIsSuccess(true)
// Redirect to signin after 3 seconds
// Redirect to signin after 3 seconds with proper URL encoding
setTimeout(() => {
if (typeof window !== 'undefined') {
router.push("/signin?message=Please check your email to verify your account")
const message = encodeURIComponent("Please check your email to verify your account")
try {
router.push(`/signin?message=${message}`)
} catch (redirectError) {
console.error('Signup redirect failed:', redirectError)
// Fallback redirect
window.location.href = `/signin?message=${message}`
}
}, 3001)
}
}, 3000)
}
if (isSuccess) {

View File

@ -1,28 +1,14 @@
/**
* Centralized Backend Configuration
* Single source of truth for all backend URLs
*/
// Main backend URL - change this to update all API calls
// LIVE PRODUCTION URL (Currently Active)
export const BACKEND_URL = 'https://backend.codenuk.com';
//
// export const BACKEND_URL = 'https://backend.codenuk.com';
// LOCAL DEVELOPMENT URL (Uncomment this and comment above for local development)
// export const BACKEND_URL = 'http://localhost:8000';
export const BACKEND_URL = 'http://localhost:8000';
// Realtime notifications socket URL (Template Manager emits notifications)
// Uses the same URL as BACKEND_URL for consistency
export const SOCKET_URL = BACKEND_URL;
// Export for backward compatibility
export const API_BASE_URL = BACKEND_URL;
// Helper function to get full API endpoint URL
export const getApiUrl = (endpoint: string): string => {
// Remove leading slash if present to avoid double slashes
const cleanEndpoint = endpoint.startsWith('/') ? endpoint.slice(1) : endpoint;
return `${BACKEND_URL}/${cleanEndpoint}`;
};

View File

@ -30,7 +30,6 @@ export function AdminNotificationProvider({ children }: { children: React.ReactN
onAllNotificationsRead
} = useNotificationSocket();
// Load initial notifications
const refreshNotifications = async () => {
try {
setIsLoading(true);
@ -43,7 +42,6 @@ export function AdminNotificationProvider({ children }: { children: React.ReactN
}
};
// Mark single notification as read
const markAsRead = async (id: string) => {
try {
await adminApi.markNotificationAsRead(id);
@ -56,7 +54,6 @@ export function AdminNotificationProvider({ children }: { children: React.ReactN
}
};
// Mark all notifications as read
const markAllAsRead = async () => {
try {
await adminApi.markAllNotificationsAsRead();
@ -69,18 +66,16 @@ export function AdminNotificationProvider({ children }: { children: React.ReactN
}
};
// Clear all notifications from UI (after marking as read server-side)
const clearAll = async () => {
await markAllAsRead();
setNotifications([]);
};
// Remove notifications tied to a specific backend reference (e.g., feature/template id)
const removeByReference = (referenceType: string, referenceId: string) => {
setNotifications(prev => prev.filter(n => !(n.reference_type === referenceType && n.reference_id === referenceId)));
};
// Set up WebSocket event handlers
useEffect(() => {
onNewNotification((notification: AdminNotification) => {
setNotifications(prev => [notification, ...prev]);
@ -99,13 +94,11 @@ export function AdminNotificationProvider({ children }: { children: React.ReactN
});
}, [onNewNotification, onNotificationRead, onAllNotificationsRead]);
// Load notifications on mount
useEffect(() => {
refreshNotifications();
}, []);
// When socket connects (e.g., navigating to admin), refresh to include any
// notifications created before the connection was established
useEffect(() => {
if (isConnected) {
refreshNotifications();

View File

@ -40,7 +40,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
timestamp: new Date().toISOString()
})
// Only restore user if we have both user data AND at least one token
if (stored && (accessToken || refreshToken)) {
try {
const userData = JSON.parse(stored)
@ -53,7 +52,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
}
} else {
console.log('🔐 [AuthContext] No valid user session found')
// Clear any partial data
if (stored && !accessToken && !refreshToken) {
console.log('🔐 [AuthContext] Clearing orphaned user data without tokens')
safeLocalStorage.removeItem('codenuk_user')
@ -62,7 +60,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
setIsLoading(false)
}, [])
// Listen for storage changes to sync auth state across tabs
useEffect(() => {
const handleStorageChange = (e: StorageEvent) => {
if (e.key === 'codenuk_user' || e.key === 'accessToken' || e.key === 'refreshToken') {
@ -99,20 +96,19 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const logout = async () => {
try {
// Call the logout API to invalidate tokens on the server
await logoutApi()
} catch (error) {
console.error('Logout API call failed:', error)
// Continue with local logout even if API call fails
} finally {
// Always clear local data
setUser(null)
safeLocalStorage.removeItem('codenuk_user')
safeLocalStorage.removeItem('accessToken')
safeLocalStorage.removeItem('refreshToken')
// Redirect to signin page
window.location.href = '/signin'
// Prevent redirect loops and add error context
if (typeof window !== 'undefined' && !window.location.pathname.includes('/signin')) {
window.location.href = '/signin?error=You have been logged out'
}
}
}

View File

@ -33,8 +33,7 @@ export function useNotificationSocket(): UseNotificationSocketReturn {
const allNotificationsReadCallbackRef = useRef<(() => void) | null>(null);
useEffect(() => {
// Initialize socket connection
const templateManagerUrl = SOCKET_URL; // connect directly to template-manager socket server
const templateManagerUrl = SOCKET_URL;
const token = getAccessToken();
console.log('[useNotificationSocket] Initializing socket', {
url: templateManagerUrl,

View File

@ -6,7 +6,6 @@ import { useAuth } from '@/contexts/auth-context';
export function useTemplates() {
const { user, isLoading: authLoading } = useAuth();
// Log user ID for debugging
useEffect(() => {
console.log('🔍 [useTemplates] Current user ID:', user?.id || 'No user ID')
console.log('👤 [useTemplates] User object:', user)
@ -17,7 +16,6 @@ export function useTemplates() {
console.log('🔐 [useTemplates] Auth loading:', authLoading)
console.log('🔐 [useTemplates] Timestamp:', new Date().toISOString())
// Check if we have tokens available
const accessToken = localStorage.getItem('accessToken')
const refreshToken = localStorage.getItem('refreshToken')
console.log('🔐 [useTemplates] Token availability:', {
@ -36,12 +34,11 @@ export function useTemplates() {
pagination?: { total?: number; limit?: number; offset?: number; hasMore?: boolean };
} | null>(null);
// Stable categories fetched once, independent of paginated data
const [categories, setCategories] = useState<Array<{ id: string; name: string; count: number }>>([]);
const [paginationState, setPaginationState] = useState({
currentPage: 0,
pageSize: 6, // Respect backend pagination: 6 items per page
pageSize: 6,
total: 0,
hasMore: false,
loading: false,
@ -51,7 +48,6 @@ export function useTemplates() {
const { show } = useToast();
// Fetch templates with pagination, category, and search
const fetchTemplatesWithPagination = useCallback(async (opts?: {
page?: number;
pageSize?: number;
@ -59,13 +55,11 @@ export function useTemplates() {
search?: string;
resetPagination?: boolean;
}) => {
// Don't fetch if auth is still loading
if (authLoading) {
console.log('[useTemplates] Auth still loading, skipping fetch')
return;
}
// Check if we have authentication tokens available
const accessToken = localStorage.getItem('accessToken')
const refreshToken = localStorage.getItem('refreshToken')
const hasTokens = !!(accessToken || refreshToken)
@ -78,13 +72,10 @@ export function useTemplates() {
userId: user?.id
})
// If user exists but no tokens, wait a bit for tokens to be loaded
if (user && !hasTokens) {
console.log('[useTemplates] User exists but no tokens found, waiting for token loading...')
// Wait a short time for tokens to be loaded from localStorage
await new Promise(resolve => setTimeout(resolve, 100))
// Check again after waiting
const retryAccessToken = localStorage.getItem('accessToken')
const retryRefreshToken = localStorage.getItem('refreshToken')
const retryHasTokens = !!(retryAccessToken || retryRefreshToken)
@ -105,7 +96,7 @@ export function useTemplates() {
const { page = 0, pageSize = paginationState.pageSize, category = paginationState.selectedCategory, search = paginationState.searchQuery, resetPagination = false } = opts || {};
const offset = resetPagination ? 0 : page * pageSize;
const limit = pageSize; // Use pageSize for limit
const limit = pageSize;
setPaginationState((prev) => ({ ...prev, loading: true }));
setError(null);
@ -116,7 +107,6 @@ export function useTemplates() {
console.log('[useTemplates] User object:', user);
console.log('[useTemplates] includeOthers will be:', !user?.id);
// Only pass userId if it is a valid UUID v4; otherwise, omit and include others
const uuidV4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
const validUserId = typeof user?.id === 'string' && uuidV4Regex.test(user.id) ? user.id : undefined;
if (!validUserId && user?.id) {
@ -125,7 +115,7 @@ export function useTemplates() {
const res = await templateService.getCombinedTemplates({
userId: validUserId,
includeOthers: true, // always include custom templates from others + defaults
includeOthers: true,
limit: pageSize,
offset,
category: category === 'all' ? undefined : category,

View File

@ -78,11 +78,9 @@ export const AI_MOCKUP_CONFIG = {
// Export the main API base URL for backward compatibility
export const API_BASE_URL = API_CONFIG.BASE_URL;
// Temporary debug logging
// Debug logging - shows centralized config is working
console.log('🔧 API Config Debug:', {
'process.env.NEXT_PUBLIC_API_URL': process.env.NEXT_PUBLIC_API_URL,
'process.env.NEXT_PUBLIC_AUTH_API_URL': process.env.NEXT_PUBLIC_AUTH_API_URL,
'DEFAULT_API_BASE_URL': DEFAULT_API_BASE_URL,
'BACKEND_URL (centralized)': BACKEND_URL,
'API_CONFIG.BASE_URL': API_CONFIG.BASE_URL,
'API_CONFIG.AUTH_SERVICE': API_CONFIG.AUTH_SERVICE,
'API_BASE_URL': API_BASE_URL