frontend changes
This commit is contained in:
parent
906f763a78
commit
4983f7a2e6
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,11 +48,17 @@ export function SignInForm({ }: SignInFormProps) {
|
||||
// Persist for refresh
|
||||
localStorage.setItem("codenuk_user", JSON.stringify(response.data.user))
|
||||
|
||||
// Redirect based on user role
|
||||
if (response.data.user.role === 'admin') {
|
||||
router.push("/admin")
|
||||
} else {
|
||||
router.push("/")
|
||||
// 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.")
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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}`;
|
||||
};
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user