PolicyRoles.tsx 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import { sortBy } from 'lodash'
  2. import {
  3. MultiSelector,
  4. MultiSelectorContent,
  5. MultiSelectorItem,
  6. MultiSelectorList,
  7. MultiSelectorTrigger,
  8. } from 'ui-patterns/multi-select'
  9. import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
  10. import { SYSTEM_ROLES } from '@/components/interfaces/Database/Roles/Roles.constants'
  11. import AlertError from '@/components/ui/AlertError'
  12. import { useDatabaseRolesQuery } from '@/data/database-roles/database-roles-query'
  13. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  14. interface PolicyRolesProps {
  15. selectedRoles: string[]
  16. onUpdateSelectedRoles: (roles: string[]) => void
  17. }
  18. type SystemRole = (typeof SYSTEM_ROLES)[number]
  19. export const PolicyRoles = ({ selectedRoles, onUpdateSelectedRoles }: PolicyRolesProps) => {
  20. const { data: project } = useSelectedProjectQuery()
  21. const {
  22. data,
  23. error,
  24. isPending: isLoading,
  25. isError,
  26. isSuccess,
  27. } = useDatabaseRolesQuery({
  28. projectRef: project?.ref,
  29. connectionString: project?.connectionString,
  30. })
  31. const roles = sortBy(
  32. (data ?? []).filter((role) => !SYSTEM_ROLES.includes(role.name as SystemRole)),
  33. (r) => r.name.toLocaleLowerCase()
  34. )
  35. const formattedRoles = roles.map((role) => {
  36. return {
  37. id: role.id,
  38. name: role.name,
  39. value: role.name,
  40. disabled: false,
  41. }
  42. })
  43. return (
  44. <div className="flex flex-col md:flex-row gap-4 md:gap-12">
  45. <div className="flex md:w-1/3 flex-col space-y-2">
  46. <label className="text-foreground-light text-base" htmlFor="policy-name">
  47. Target roles
  48. </label>
  49. <p className="text-foreground-lighter text-sm">Apply policy to the selected roles</p>
  50. </div>
  51. <div className="relative md:w-2/3">
  52. {isLoading && <ShimmeringLoader className="py-4" />}
  53. {isError && <AlertError error={error as any} subject="Failed to retrieve database roles" />}
  54. {isSuccess && (
  55. <MultiSelector values={selectedRoles} onValuesChange={onUpdateSelectedRoles}>
  56. <MultiSelectorTrigger
  57. mode="inline-combobox"
  58. label={
  59. selectedRoles.length === 0
  60. ? 'Defaults to all (public) roles if none selected'
  61. : 'Search for a role'
  62. }
  63. deletableBadge
  64. badgeLimit="wrap"
  65. showIcon={false}
  66. />
  67. <MultiSelectorContent>
  68. <MultiSelectorList>
  69. {formattedRoles.map((role) => (
  70. <MultiSelectorItem key={role.id} value={role.value} disabled={role.disabled}>
  71. {role.name}
  72. </MultiSelectorItem>
  73. ))}
  74. </MultiSelectorList>
  75. </MultiSelectorContent>
  76. </MultiSelector>
  77. )}
  78. </div>
  79. </div>
  80. )
  81. }