Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
167 commits
Select commit Hold shift + click to select a range
3c60f52
refactor: migrate project settings to TS and break into smaller compo…
talissoncosta Nov 19, 2025
3fb783c
refactor: standardize organisationId type and improve error handling
talissoncosta Nov 19, 2025
9787499
refactor: add check on update func and clean up comments
talissoncosta Nov 19, 2025
441fe25
feat: add small transition on hide/show component
talissoncosta Nov 19, 2025
e31e205
fix: add disable state for delete project button
talissoncosta Nov 19, 2025
30c96be
fix: remove unused types
talissoncosta Nov 19, 2025
3bb76e2
fix: fix permissions data
talissoncosta Nov 19, 2025
c076140
refactor: simplify import tab passing project name and id
talissoncosta Nov 19, 2025
590ba2e
refactor: create useUpdateProjectWithToast custom hook
talissoncosta Nov 19, 2025
b561723
refactor: migrate settings components to use custom hook
talissoncosta Nov 19, 2025
5dc862b
refactor: use Pick for UpdateProjectBody type definition
talissoncosta Nov 20, 2025
38a9644
feat: add optimistic updates to project mutation
talissoncosta Nov 20, 2025
d7ee032
refactor: remove manual state syncing from project settings components
talissoncosta Nov 20, 2025
414c9ce
refactor: extract change requests setting into separate component
talissoncosta Nov 20, 2025
bd3d40b
refactor: remove unnecessary useCallback wrappers
talissoncosta Nov 20, 2025
5717852
refactor: small adjusts
talissoncosta Nov 20, 2025
391922d
fix: sync OrganisationStore after project updates for navbar refresh
talissoncosta Nov 20, 2025
0065414
fix: address PR review comments - type safety and code organization
talissoncosta Nov 20, 2025
bd6b2a1
fix: don't re-throw error
talissoncosta Nov 20, 2025
7e30d7b
fix: adjust index files on internal pages
talissoncosta Nov 21, 2025
4aaf163
fix: restore original toast messages and button states in project set…
talissoncosta Nov 21, 2025
e63638f
fix: correct inverted logic in case sensitivity toggle
talissoncosta Nov 21, 2025
6437dcd
fix: resolve state and persistence issues in ChangeRequestsApprovalsS…
talissoncosta Nov 24, 2025
f418d8b
feat: add data-test prop support to ChangeRequestsSetting
talissoncosta Nov 24, 2025
2066337
test: add comprehensive E2E coverage for change request approvals
talissoncosta Nov 24, 2025
fd418ba
fix: add space between label and input on stale flag detection
talissoncosta Nov 24, 2025
4fe578a
refactor: wrap change requests e2e tests in conditional block for sca…
talissoncosta Nov 24, 2025
5fe5568
feat: add organisation API request types
talissoncosta Nov 20, 2025
1e1ea70
feat: add RTK Query mutations for organisation management
talissoncosta Nov 20, 2025
cdfbbce
feat: add organisation settings custom hooks
talissoncosta Nov 20, 2025
1b7d9c7
feat: create General tab section components
talissoncosta Nov 20, 2025
82100f9
feat: create General tab orchestrator component
talissoncosta Nov 20, 2025
509ab4a
feat: create tab wrapper components
talissoncosta Nov 20, 2025
be9169a
feat: create main OrganisationSettingsPage with RTK Query
talissoncosta Nov 20, 2025
2e6a318
refactor: migrate OrganisationSettingsPage to TypeScript
talissoncosta Nov 20, 2025
b9232ee
fix: spacing on organization name input and button
talissoncosta Nov 21, 2025
0f77538
refactor: remove AccountStore dependency from DeleteOrganisation
talissoncosta Nov 21, 2025
407ce1b
fix: add AppActions.refreshOrganisation for backward compatibility
talissoncosta Nov 21, 2025
c03885d
feat: add loading state to Delete Organisation button
talissoncosta Nov 21, 2025
b8eb3de
refactor: remove unnecessary comments
talissoncosta Nov 21, 2025
603f688
fix: add name as required on body type
talissoncosta Nov 21, 2025
109af81
fix: restore organisation-name data-test selector
talissoncosta Nov 21, 2025
2b0449a
fix: restore original text content and styling
talissoncosta Nov 21, 2025
7a5d3f7
fix: add missing 'organisation' type to Res type
talissoncosta Nov 24, 2025
683bfcb
refactor: improve cache invalidation in useOrganisation
talissoncosta Nov 24, 2025
e06b032
fix: restore /create fallback and improve component structure
talissoncosta Nov 24, 2025
2d7b53f
refactor: standardize organisation ID types to number
talissoncosta Nov 24, 2025
556846a
Merge remote-tracking branch 'origin/main' into refactor/migrate-orga…
talissoncosta Nov 25, 2025
616ecb0
fix: adjust based on feedback
talissoncosta Nov 25, 2025
1c84b73
tests: add test to check name is updated
talissoncosta Nov 26, 2025
42abf2c
fix: get updated org from rtk with store as fallback
talissoncosta Nov 26, 2025
39bfcd2
refactor: migrate BreadcrumbSeparator to RTK Query
talissoncosta Nov 26, 2025
63c2dc0
fix: invalidate RTK Query cache after organization creation
talissoncosta Nov 26, 2025
caf97c5
fix: replace refreshOrganisation with selectOrganisation after deletion
talissoncosta Nov 26, 2025
dadd750
fix: add getOrganisation call after org deletion
talissoncosta Nov 26, 2025
ba72cca
fix: invalidate cache when creating a new org
talissoncosta Nov 26, 2025
cd98ea3
tests: add new tests on organization settings
talissoncosta Nov 26, 2025
655dae4
test: remove flaky tests
talissoncosta Nov 26, 2025
caf7150
fix: add missing waitForElementNotExist import and wait in Test 3
talissoncosta Nov 26, 2025
0439174
refactor: extract empty state and metrics components from FeaturesPage
talissoncosta Nov 21, 2025
6ae3524
refactor: extract FeaturesTableFilters as pure component without stor…
talissoncosta Nov 25, 2025
d635339
Merge remote-tracking branch 'origin/main' into refactor/migrate-crit…
talissoncosta Nov 28, 2025
277ffaf
Merge remote-tracking branch 'origin/main' into refactor/migrate-crit…
talissoncosta Dec 2, 2025
c01874f
Merge branch 'main' into refactor/migrate-critical-components-feature…
talissoncosta Dec 2, 2025
271cc98
refactor: rm unnecessary deps
talissoncosta Dec 2, 2025
95a864a
Merge remote-tracking branch 'origin/main' into refactor/migrate-crit…
talissoncosta Dec 2, 2025
d8a9f93
feat: add RTK Query service for feature list with comprehensive filte…
talissoncosta Nov 26, 2025
a6177f0
feat: integrate RTK Query in FeaturesTableFilters for real-time data …
talissoncosta Nov 26, 2025
af310f4
feat: migrate FeaturesPage to functional component with full RTK Quer…
talissoncosta Nov 26, 2025
2b4abf8
chore(types): add global type declarations for legacy utils
talissoncosta Nov 27, 2025
63cab70
refactor(features): extract custom hooks for filters and actions
talissoncosta Nov 27, 2025
2ac59ef
refactor(features): extract UI components from FeaturesPage
talissoncosta Nov 27, 2025
c4079f5
refactor(features): simplify FeaturesPage using custom hooks and comp…
talissoncosta Nov 27, 2025
420fbd4
feat(features): add shared TypeScript types and useProjectEnvironment…
talissoncosta Dec 2, 2025
22e6ad8
refactor(features): replace 'any' types with proper TypeScript types …
talissoncosta Dec 2, 2025
dddaca0
refactor(features): remove legacy stores from FeaturesList component
talissoncosta Dec 2, 2025
277deb9
refactor(features): eliminate legacy stores from FeaturesPage
talissoncosta Dec 2, 2025
38d5b62
fix(features): invalidate METRICS cache on feature state mutations
talissoncosta Dec 2, 2025
0085403
refactor(features): remove manual metrics refetch mechanism
talissoncosta Dec 2, 2025
8b5251e
refactor(features): remove redundant comments
talissoncosta Dec 2, 2025
9ec2f89
feat(features): add RTK Query service for feature list with comprehen…
talissoncosta Dec 3, 2025
16f2cbc
refactor(features): migrate FeaturesPage to functional component with…
talissoncosta Dec 3, 2025
d4bfb68
refactor(features): eliminate legacy stores and improve type safety
talissoncosta Dec 3, 2025
8c6c748
refactor(features): extract filter logic to pure utility functions
talissoncosta Dec 3, 2025
0a45438
refactor(features): improve code organization and type safety
talissoncosta Dec 3, 2025
984b561
refactor(features): centralize pagination config and fix type issues
talissoncosta Dec 3, 2025
d300df8
fix(metrics): prevent API calls with undefined environmentApiKey
talissoncosta Dec 3, 2025
c32e191
Merge remote-tracking branch 'origin/main' into refactor/migrate-crit…
talissoncosta Dec 3, 2025
aec1b56
fix(types): resolve TypeScript errors in useFeatureList service
talissoncosta Dec 3, 2025
e8100ba
fix(types): add Environment tag type for cross-service cache invalida…
talissoncosta Dec 3, 2025
b500e76
refactor(features): remove complex optimistic update logic
talissoncosta Dec 3, 2025
839cff1
refactor(types): add explicit update body types following team pattern
talissoncosta Dec 3, 2025
455e49f
refactor(types): standardize projectId type to number across components
talissoncosta Dec 3, 2025
032e538
fix(types): align pagination field naming with component expectations
talissoncosta Dec 3, 2025
f1f7e94
refactor(features): simplify FeaturesPage to use RouteContext exclusi…
talissoncosta Dec 3, 2025
fec4d01
fix(features): standardize projectId type to number across FeaturesPa…
talissoncosta Dec 3, 2025
63dd7ea
refactor(features): remove unused props and improve naming
talissoncosta Dec 3, 2025
5e7e4a9
refactor(features): rename stateId to environmentFlagId for clarity
talissoncosta Dec 3, 2025
b4c546c
refactor(features): standardize removeProjectFlag parameter naming
talissoncosta Dec 3, 2025
67f2727
refactor(features): extract filter utilities to global scope for reuse
talissoncosta Dec 4, 2025
e575498
refactor(features): extract hooks to global scope for reuse
talissoncosta Dec 4, 2025
ab02cc1
feat(features): add useFeatureListWithFilters hook for RTK Query inte…
talissoncosta Dec 4, 2025
bdff671
refactor(features): split feature actions into focused hooks with toa…
talissoncosta Dec 4, 2025
20dc34e
refactor(features): reorganize FeaturesPage component structure
talissoncosta Dec 4, 2025
c17fde0
refactor(features): extract display logic to useFeaturePageDisplay hook
talissoncosta Dec 4, 2025
811b140
refactor(features): migrate FeaturesPage from Flux to RTK Query
talissoncosta Dec 4, 2025
2121040
refactor(features): update supporting components for RTK Query migration
talissoncosta Dec 4, 2025
286362a
feat(components): add PermissionGate and update metrics components
talissoncosta Dec 4, 2025
de9a5d4
refactor(features): remove FeaturesList wrapper component
talissoncosta Dec 4, 2025
ae18579
refactor(features): extract inline render functions to named constants
talissoncosta Dec 4, 2025
d643b49
refactor(features): extract header render to named constant
talissoncosta Dec 4, 2025
f33fcc8
refactor(features): remove legacy Flux store ignore filter
talissoncosta Dec 4, 2025
543a386
refactor(features): extract pagination handlers and remove temporary …
talissoncosta Dec 4, 2025
ca859e5
fix(features): correct FilterState import in useFeaturePageDisplay
talissoncosta Dec 4, 2025
9207690
perf(features): add memoization to prevent unnecessary re-renders
talissoncosta Dec 4, 2025
bfe783a
feat(features): add error handling UI for failed data fetching
talissoncosta Dec 4, 2025
dfe50c3
refactor(features): remove unused legacy types from types.ts
talissoncosta Dec 4, 2025
9fae3b0
refactor(features): replace custom error UI with reusable ErrorMessag…
talissoncosta Dec 4, 2025
a7a4c32
fix(features): correct error handling placement and logic
talissoncosta Dec 4, 2025
63747eb
feat(features): improve error message UX with user-friendly context
talissoncosta Dec 4, 2025
0efba2a
feat(features): add skeleton loader for features list
talissoncosta Dec 4, 2025
0345bdb
refactor(features): simplify display logic and remove useFeaturePageD…
talissoncosta Dec 4, 2025
92c10a4
refactor(features): remove unused showCreateButton prop from Features…
talissoncosta Dec 4, 2025
69c2df2
docs: remove excessive documentation from hooks and utilities
talissoncosta Dec 4, 2025
63863e6
docs: simplify documentation in useProjectEnvironments and featureFil…
talissoncosta Dec 4, 2025
97db45b
refactor(features): clean up FeaturesTableFilters and optimize useFea…
talissoncosta Dec 4, 2025
f526820
tests: keep backwards compatibility on creating a new flag
talissoncosta Dec 5, 2025
c38567d
fix(features): add backward compatibility and cache sync for CreateFl…
talissoncosta Dec 5, 2025
83a2639
Merge remote-tracking branch 'origin/main' into refactor/migrate-crit…
talissoncosta Dec 5, 2025
5d80d29
fix: lint
talissoncosta Dec 5, 2025
8f858e1
fix(features): prevent empty state flash and ensure proper cache sync
talissoncosta Dec 5, 2025
30f4094
refactor: clean up dead code and make internal helpers non-exported
talissoncosta Dec 5, 2025
d8bf596
refactor: convert TagStrategy type to enum
talissoncosta Dec 5, 2025
7c18d34
refactor: add type-safe URL parameter parsing helpers to featureFilte…
talissoncosta Dec 5, 2025
6e24a23
feat: add useFeatureListWithApiKey wrapper hook
talissoncosta Dec 5, 2025
76084a1
refactor: clean up unnecessary and verbose comments
talissoncosta Dec 5, 2025
496afe4
fix: address PR review feedback (typo and parseInt radix)
talissoncosta Dec 5, 2025
3fcad5a
feat(features): add dark mode and improve FeatureRowSkeleton structure
talissoncosta Dec 8, 2025
30f6b79
Merge remote-tracking branch 'origin/main' into refactor/migrate-crit…
talissoncosta Dec 9, 2025
51e67c9
fix: show "no results" instead of empty state when filters return no …
talissoncosta Dec 9, 2025
84e1c3d
fix: prevent search input interruption during loading
talissoncosta Dec 9, 2025
77f85c8
refactor: remove unnecessary comments from FeaturesPage
talissoncosta Dec 9, 2025
cb597b9
refactor(services): consolidate useFeatureList into useProjectFlag
talissoncosta Dec 9, 2025
85a3e1e
refactor(config): move FEATURES_PAGE_SIZE to service layer
talissoncosta Dec 9, 2025
e663a16
refactor(hooks): consolidate page tracking hooks
talissoncosta Dec 9, 2025
cf9cbc7
chore: remove unnecessary comments from useProjectFlag
talissoncosta Dec 9, 2025
826c010
fix: resolve loading flow issues and filter parameter handling
talissoncosta Dec 10, 2025
2d79857
refactor: remove unnecessary comments and duplicate code
talissoncosta Dec 10, 2025
735cb2b
fix: improve loading flow and skeleton display in FeaturesPage
talissoncosta Dec 10, 2025
2299462
refactor: remove unused multivariate option type definitions
talissoncosta Dec 10, 2025
6d8fb5a
refactor: migrate to unified usePageTracking hook pattern
talissoncosta Dec 10, 2025
fe6a05e
docs: remove documentation file reference from code comment
talissoncosta Dec 10, 2025
ce88590
refactor: simplify Utils type definition using typeof
talissoncosta Dec 10, 2025
c705d7d
refactor: move updateFeatureState mutation to useFeatureState service
talissoncosta Dec 10, 2025
306bfca
refactor: consolidate PermissionGate into Permission component
talissoncosta Dec 10, 2025
c89dd4f
chore: remove unnecessary comments from refactored files
talissoncosta Dec 10, 2025
aa89314
docs: add comprehensive JSDoc to Permission component
talissoncosta Dec 10, 2025
0e331ab
fix(hooks): prevent excessive re-renders in usePageTracking
talissoncosta Dec 11, 2025
b69386a
fix(services): invalidate environment metrics cache on feature removal
talissoncosta Dec 11, 2025
67bccc5
fix(providers): enable tooltips for Permission component with render …
talissoncosta Dec 11, 2025
336502c
fix: adapt callback signatures in Compare components for FeatureRow c…
talissoncosta Dec 11, 2025
53e5b7a
fix: handle project/environment errors to prevent infinite loading state
talissoncosta Dec 12, 2025
a176fa9
Merge remote-tracking branch 'origin/main' into refactor/migrate-crit…
talissoncosta Dec 15, 2025
2bd4560
fix(features): add permissionName to Permission tooltip for readable UX
talissoncosta Dec 15, 2025
3e522b2
fix(features): resolve Cursor bot issues for sort order and permissio…
talissoncosta Dec 15, 2025
11a1dfe
refactor(features): remove unnecessary ConfigProvider HOC
talissoncosta Dec 15, 2025
bca9d30
fix(features): fix object spread overriding default pagination values
talissoncosta Dec 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions frontend/common/hooks/useFeatureListWithApiKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useMemo } from 'react'
import { skipToken } from '@reduxjs/toolkit/query'
import { useGetFeatureListQuery } from 'common/services/useProjectFlag'
import { useProjectEnvironments } from './useProjectEnvironments'
import { buildApiFilterParams } from 'common/utils/featureFilterParams'
import type { FilterState } from 'common/types/featureFilters'

/**
* Fetches filtered feature list, accepting environment API key instead of numeric ID.
*
* TODO: This wrapper will be removed once we standardize environmentId and environmentApiKey on RouteContext.
*/
export function useFeatureListWithApiKey(
filters: FilterState,
page: number,
environmentApiKey: string | undefined,
projectId: number | undefined,
): ReturnType<typeof useGetFeatureListQuery> {
const { getEnvironmentIdFromKey, isLoading: isLoadingEnvironments } =
useProjectEnvironments(projectId!)

const apiParams = useMemo(() => {
if (!environmentApiKey || !projectId || !getEnvironmentIdFromKey) {
return null
}
return buildApiFilterParams(
filters,
page,
environmentApiKey,
projectId,
getEnvironmentIdFromKey,
)
}, [filters, page, environmentApiKey, projectId, getEnvironmentIdFromKey])

return useGetFeatureListQuery(apiParams ?? skipToken, {
refetchOnMountOrArgChange: true,
skip: isLoadingEnvironments,
})
}
84 changes: 84 additions & 0 deletions frontend/common/hooks/usePageTracking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useEffect } from 'react'

/**
* Options for configuring page tracking behavior.
*/
export type PageTrackingOptions = {
/** The page constant name from Constants.pages */
pageName: string
/** Context data for tracking and storage persistence */
context?: {
environmentId?: string
projectId?: number
organisationId?: number
}
/** Whether to save context to AsyncStorage (default: false) */
saveToStorage?: boolean
/** Custom dependencies for re-tracking on changes */
deps?: React.DependencyList
}

/**
* Unified hook for tracking page views with optional context persistence.
*
* Consolidates both page tracking and environment context storage into a single,
* flexible hook. Automatically calls API.trackPage and optionally persists
* environment context to AsyncStorage.
*
* @param options - Configuration object for page tracking
*
* @example
* ```tsx
* // Basic page tracking only
* usePageTracking({ pageName: Constants.pages.FEATURES })
*
* // With context and storage persistence
* usePageTracking({
* pageName: Constants.pages.FEATURES,
* context: { environmentId, projectId, organisationId },
* saveToStorage: true,
* })
*
* // With custom dependencies
* usePageTracking({
* pageName: Constants.pages.FEATURES,
* context: { projectId },
* deps: [projectId, someOtherDep],
* })
* ```
*/
export function usePageTracking(options: PageTrackingOptions): void {
const { context, deps = [], pageName, saveToStorage = false } = options

// Track page view
useEffect(() => {
if (typeof API !== 'undefined' && API.trackPage) {
API.trackPage(pageName)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps)

// Persist environment context to storage if enabled
useEffect(() => {
if (saveToStorage && context) {
if (typeof AsyncStorage !== 'undefined' && AsyncStorage.setItem) {
AsyncStorage.setItem(
'lastEnv',
JSON.stringify({
environmentId: context.environmentId,
orgId: context.organisationId,
projectId: context.projectId,
}),
)
}
}
// We intentionally use individual properties instead of context object
// to prevent re-runs when object reference changes but values don't
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
saveToStorage,
context?.environmentId,
context?.organisationId,
context?.projectId,
])
}
58 changes: 58 additions & 0 deletions frontend/common/hooks/useProjectEnvironments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useCallback, useMemo } from 'react'
import { useGetEnvironmentsQuery } from 'common/services/useEnvironment'
import { useGetProjectQuery } from 'common/services/useProject'
import type { Environment, Project } from 'common/types/responses'

interface UseProjectEnvironmentsResult {
project: Project | undefined
environments: Environment[]
getEnvironmentIdFromKey: (apiKey: string) => number | undefined
getEnvironment: (apiKey: string) => Environment | undefined
isLoading: boolean
error: Error | undefined
}

/** Fetches project and environment data with accessor functions for API key lookups. */
export function useProjectEnvironments(
projectId: number,
): UseProjectEnvironmentsResult {
const {
data: project,
error: projectError,
isLoading: isLoadingProject,
} = useGetProjectQuery({ id: projectId }, { skip: !projectId })

const {
data: environmentsData,
error: environmentsError,
isLoading: isLoadingEnvironments,
} = useGetEnvironmentsQuery({ projectId }, { skip: !projectId })

const environments = useMemo(
() => environmentsData?.results ?? [],
[environmentsData?.results],
)

const getEnvironmentIdFromKey = useCallback(
(apiKey: string): number | undefined => {
return environments.find((env) => env.api_key === apiKey)?.id
},
[environments],
)

const getEnvironment = useCallback(
(apiKey: string): Environment | undefined => {
return environments.find((env) => env.api_key === apiKey)
},
[environments],
)

return {
environments,
error: projectError || environmentsError,
getEnvironment,
getEnvironmentIdFromKey,
isLoading: isLoadingProject || isLoadingEnvironments,
project,
}
}
138 changes: 128 additions & 10 deletions frontend/common/providers/Permission.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,51 @@ import { PermissionLevel } from 'common/types/requests'
import AccountStore from 'common/stores/account-store'
import intersection from 'lodash/intersection'
import { cloneDeep } from 'lodash'
import Utils from 'common/utils/utils'
import Constants from 'common/constants'

/**
* Props for the Permission component
*
* @property {number | string} id - The ID of the resource (projectId, organisationId, environmentId, etc.)
* @property {string} permission - The permission key to check (e.g., 'CREATE_FEATURE', 'UPDATE_FEATURE')
* @property {PermissionLevel} level - The permission level ('project', 'organisation', 'environment')
* @property {number[]} [tags] - Optional tag IDs for tag-based permission checking
* @property {ReactNode | ((data: { permission: boolean; isLoading: boolean }) => ReactNode)} children - Content to render or render function
* @property {ReactNode} [fallback] - Optional content to render when permission is denied
* @property {string} [permissionName] - Optional custom permission name for tooltip display
* @property {boolean} [showTooltip=false] - Whether to show a tooltip when permission is denied
*/
type PermissionType = {
id: any
id: number | string
permission: string
tags?: number[]
level: PermissionLevel
children: (data: { permission: boolean; isLoading: boolean }) => ReactNode
children:
| ReactNode
| ((data: { permission: boolean; isLoading: boolean }) => ReactNode)
fallback?: ReactNode
permissionName?: string
showTooltip?: boolean
}

/**
* Hook to check if the current user has a specific permission
*
* Fetches permission data and checks if the user has the requested permission.
* Supports tag-based permissions where additional permissions can be granted
* based on tag intersection.
*
* @param {Object} params - The permission check parameters
* @param {number | string} params.id - The resource ID to check permissions for
* @param {PermissionLevel} params.level - The permission level to check at
* @param {string} params.permission - The permission key to check
* @param {number[]} [params.tags] - Optional tag IDs for tag-based permission checking
* @returns {Object} Object containing permission status and loading state
* @returns {boolean} returns.isLoading - Whether the permission data is still loading
* @returns {boolean} returns.isSuccess - Whether the permission data was fetched successfully
* @returns {boolean} returns.permission - Whether the user has the requested permission
*/
export const useHasPermission = ({
id,
level,
Expand Down Expand Up @@ -45,11 +81,68 @@ export const useHasPermission = ({
}
}

/**
* Permission component for conditional rendering based on user permissions
*
* This component checks if the current user has a specific permission and conditionally
* renders its children. It supports multiple rendering patterns:
*
* @example
* // Basic usage with simple children
* <Permission level="project" permission="CREATE_FEATURE" id={projectId}>
* <Button>Create Feature</Button>
* </Permission>
*
* @example
* // Using render function to access permission state
* <Permission level="project" permission="CREATE_FEATURE" id={projectId}>
* {({ permission, isLoading }) => (
* <Button disabled={!permission || isLoading}>Create Feature</Button>
* )}
* </Permission>
*
* @example
* // With tooltip on permission denial
* <Permission
* level="project"
* permission="CREATE_FEATURE"
* id={projectId}
* showTooltip
* permissionName="Create Features"
* >
* <Button>Create Feature</Button>
* </Permission>
*
* @example
* // With fallback content
* <Permission
* level="project"
* permission="DELETE_FEATURE"
* id={projectId}
* fallback={<Text>You don't have permission to delete features</Text>}
* >
* <Button>Delete Feature</Button>
* </Permission>
*
* @example
* // With tag-based permissions
* <Permission
* level="project"
* permission="UPDATE_FEATURE"
* id={projectId}
* tags={[tagId1, tagId2]}
* >
* <Button>Update Feature</Button>
* </Permission>
*/
const Permission: FC<PermissionType> = ({
children,
fallback,
id,
level,
permission,
permissionName,
showTooltip = false,
tags,
}) => {
const { isLoading, permission: hasPermission } = useHasPermission({
Expand All @@ -58,14 +151,39 @@ const Permission: FC<PermissionType> = ({
permission,
tags,
})
return (
<>
{children({
isLoading,
permission: hasPermission || AccountStore.isAdmin(),
}) || null}
</>
)

const finalPermission = hasPermission || AccountStore.isAdmin()

if (typeof children === 'function') {
const renderedChildren = children({
isLoading,
permission: finalPermission,
})

if (finalPermission || !showTooltip) {
return <>{renderedChildren || null}</>
}

return Utils.renderWithPermission(
finalPermission,
permissionName || Constants.projectPermissions(permission),
renderedChildren,
)
}

if (finalPermission) {
return <>{children}</>
}

if (showTooltip) {
return Utils.renderWithPermission(
finalPermission,
permissionName || Constants.projectPermissions(permission),
children,
)
}

return <>{fallback || null}</>
}

export default Permission
Loading
Loading