You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

navigation.ts 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /**
  2. * Navigation Utilities
  3. *
  4. * Provides helper functions for consistent navigation behavior throughout the application,
  5. * specifically for preserving query parameters when navigating between related pages.
  6. */
  7. /**
  8. * Creates a navigation path that preserves current URL query parameters
  9. *
  10. * @param basePath - The base path to navigate to (e.g., '/datasets/123/documents')
  11. * @param preserveParams - Whether to preserve current query parameters (default: true)
  12. * @returns The complete navigation path with preserved query parameters
  13. *
  14. * @example
  15. * // Current URL: /datasets/123/documents/456?page=3&limit=10&keyword=test
  16. * const backPath = createNavigationPath('/datasets/123/documents')
  17. * // Returns: '/datasets/123/documents?page=3&limit=10&keyword=test'
  18. *
  19. * @example
  20. * // Navigate without preserving params
  21. * const cleanPath = createNavigationPath('/datasets/123/documents', false)
  22. * // Returns: '/datasets/123/documents'
  23. */
  24. export function createNavigationPath(basePath: string, preserveParams: boolean = true): string {
  25. if (!preserveParams)
  26. return basePath
  27. try {
  28. const searchParams = new URLSearchParams(window.location.search)
  29. const queryString = searchParams.toString()
  30. const separator = queryString ? '?' : ''
  31. return `${basePath}${separator}${queryString}`
  32. }
  33. catch (error) {
  34. // Fallback to base path if there's any error accessing location
  35. console.warn('Failed to preserve query parameters:', error)
  36. return basePath
  37. }
  38. }
  39. /**
  40. * Creates a back navigation function that preserves query parameters
  41. *
  42. * @param router - Next.js router instance
  43. * @param basePath - The base path to navigate back to
  44. * @param preserveParams - Whether to preserve current query parameters (default: true)
  45. * @returns A function that navigates back with preserved parameters
  46. *
  47. * @example
  48. * const router = useRouter()
  49. * const backToPrev = createBackNavigation(router, `/datasets/${datasetId}/documents`)
  50. *
  51. * // Later, when user clicks back:
  52. * backToPrev()
  53. */
  54. export function createBackNavigation(
  55. router: { push: (path: string) => void },
  56. basePath: string,
  57. preserveParams: boolean = true,
  58. ): () => void {
  59. return () => {
  60. const navigationPath = createNavigationPath(basePath, preserveParams)
  61. router.push(navigationPath)
  62. }
  63. }
  64. /**
  65. * Extracts specific query parameters from current URL
  66. *
  67. * @param paramNames - Array of parameter names to extract
  68. * @returns Object with extracted parameters
  69. *
  70. * @example
  71. * // Current URL: /page?page=3&limit=10&keyword=test&other=value
  72. * const params = extractQueryParams(['page', 'limit', 'keyword'])
  73. * // Returns: { page: '3', limit: '10', keyword: 'test' }
  74. */
  75. export function extractQueryParams(paramNames: string[]): Record<string, string> {
  76. try {
  77. const searchParams = new URLSearchParams(window.location.search)
  78. const extracted: Record<string, string> = {}
  79. paramNames.forEach((name) => {
  80. const value = searchParams.get(name)
  81. if (value !== null)
  82. extracted[name] = value
  83. })
  84. return extracted
  85. }
  86. catch (error) {
  87. console.warn('Failed to extract query parameters:', error)
  88. return {}
  89. }
  90. }
  91. /**
  92. * Creates a navigation path with specific query parameters
  93. *
  94. * @param basePath - The base path
  95. * @param params - Object of query parameters to include
  96. * @returns Navigation path with specified parameters
  97. *
  98. * @example
  99. * const path = createNavigationPathWithParams('/datasets/123/documents', {
  100. * page: '1',
  101. * limit: '25',
  102. * keyword: 'search term'
  103. * })
  104. * // Returns: '/datasets/123/documents?page=1&limit=25&keyword=search+term'
  105. */
  106. export function createNavigationPathWithParams(
  107. basePath: string,
  108. params: Record<string, string | number>,
  109. ): string {
  110. try {
  111. const searchParams = new URLSearchParams()
  112. Object.entries(params).forEach(([key, value]) => {
  113. if (value !== undefined && value !== null && value !== '')
  114. searchParams.set(key, String(value))
  115. })
  116. const queryString = searchParams.toString()
  117. const separator = queryString ? '?' : ''
  118. return `${basePath}${separator}${queryString}`
  119. }
  120. catch (error) {
  121. console.warn('Failed to create navigation path with params:', error)
  122. return basePath
  123. }
  124. }
  125. /**
  126. * Merges current query parameters with new ones
  127. *
  128. * @param newParams - New parameters to add or override
  129. * @param preserveExisting - Whether to preserve existing parameters (default: true)
  130. * @returns URLSearchParams object with merged parameters
  131. *
  132. * @example
  133. * // Current URL: /page?page=3&limit=10
  134. * const merged = mergeQueryParams({ keyword: 'test', page: '1' })
  135. * // Results in: page=1&limit=10&keyword=test (page overridden, limit preserved, keyword added)
  136. */
  137. export function mergeQueryParams(
  138. newParams: Record<string, string | number | null | undefined>,
  139. preserveExisting: boolean = true,
  140. ): URLSearchParams {
  141. const searchParams = preserveExisting
  142. ? new URLSearchParams(window.location.search)
  143. : new URLSearchParams()
  144. Object.entries(newParams).forEach(([key, value]) => {
  145. if (value === null || value === undefined)
  146. searchParams.delete(key)
  147. else if (value !== '')
  148. searchParams.set(key, String(value))
  149. })
  150. return searchParams
  151. }
  152. /**
  153. * Navigation utilities for common dataset/document patterns
  154. */
  155. export const datasetNavigation = {
  156. /**
  157. * Creates navigation back to dataset documents list with preserved state
  158. */
  159. backToDocuments: (router: { push: (path: string) => void }, datasetId: string) => {
  160. return createBackNavigation(router, `/datasets/${datasetId}/documents`)
  161. },
  162. /**
  163. * Creates navigation to document detail
  164. */
  165. toDocumentDetail: (router: { push: (path: string) => void }, datasetId: string, documentId: string) => {
  166. return () => router.push(`/datasets/${datasetId}/documents/${documentId}`)
  167. },
  168. /**
  169. * Creates navigation to document settings
  170. */
  171. toDocumentSettings: (router: { push: (path: string) => void }, datasetId: string, documentId: string) => {
  172. return () => router.push(`/datasets/${datasetId}/documents/${documentId}/settings`)
  173. },
  174. }