mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 10:28:00 +00:00
feat(ui): changed back to relative time distance with tooltip of detailed time
This commit is contained in:
@@ -49,6 +49,7 @@ const MAX_ROLES_TO_BE_SHOWN_IN_TABLE = 2;
|
|||||||
const formatRoleName = (role: string, customRoleName?: string) => {
|
const formatRoleName = (role: string, customRoleName?: string) => {
|
||||||
if (role === ProjectMembershipRole.Custom) return customRoleName;
|
if (role === ProjectMembershipRole.Custom) return customRoleName;
|
||||||
if (role === ProjectMembershipRole.Member) return "Developer";
|
if (role === ProjectMembershipRole.Member) return "Developer";
|
||||||
|
if (role === ProjectMembershipRole.NoAccess) return "No access";
|
||||||
return role;
|
return role;
|
||||||
};
|
};
|
||||||
export const IdentityTab = withProjectPermission(
|
export const IdentityTab = withProjectPermission(
|
||||||
@@ -173,7 +174,9 @@ export const IdentityTab = withProjectPermission(
|
|||||||
return (
|
return (
|
||||||
<Tag key={roleId}>
|
<Tag key={roleId}>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<div>{formatRoleName(role, customRoleName)}</div>
|
<div className="capitalize">
|
||||||
|
{formatRoleName(role, customRoleName)}
|
||||||
|
</div>
|
||||||
{isTemporary && (
|
{isTemporary && (
|
||||||
<div>
|
<div>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
|||||||
+41
-22
@@ -1,8 +1,9 @@
|
|||||||
|
/* eslint-disable no-nested-ternary */
|
||||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||||
import { faCaretDown, faClock, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons";
|
import { faCaretDown, faClock, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { format } from "date-fns";
|
import { format, formatDistance } from "date-fns";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -21,7 +22,8 @@ import {
|
|||||||
Select,
|
Select,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
Spinner,
|
Spinner,
|
||||||
Tag
|
Tag,
|
||||||
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionActions,
|
ProjectPermissionActions,
|
||||||
@@ -182,24 +184,41 @@ export const IdentityRbacSection = ({ identityProjectMember, onOpenUpgradeModal
|
|||||||
/>
|
/>
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger disabled={isMemberEditDisabled}>
|
<PopoverTrigger disabled={isMemberEditDisabled}>
|
||||||
<Button
|
<div>
|
||||||
variant="outline_bg"
|
<Tooltip
|
||||||
leftIcon={isTemporary ? <FontAwesomeIcon icon={faClock} /> : undefined}
|
content={
|
||||||
rightIcon={<FontAwesomeIcon icon={faCaretDown} className="ml-2" />}
|
temporaryAccess?.isTemporary
|
||||||
isDisabled={isMemberEditDisabled}
|
? isExpired
|
||||||
className={twMerge(
|
? "Timed Access Expired"
|
||||||
"border-none bg-mineshaft-600 hover:bg-mineshaft-500 py-2.5 capitalize text-xs",
|
: `Until ${format(
|
||||||
isTemporary && "text-primary",
|
new Date(temporaryAccess.temporaryAccessEndTime || ""),
|
||||||
isExpired && "text-red-600"
|
"yyyy-MM-dd HH:mm:ss"
|
||||||
)}
|
)}`
|
||||||
>
|
: "Non expiry access"
|
||||||
{!temporaryAccess?.isTemporary
|
}
|
||||||
? "Permanent"
|
>
|
||||||
: `Expires at ${format(
|
<Button
|
||||||
new Date(temporaryAccess.temporaryAccessEndTime || ""),
|
variant="outline_bg"
|
||||||
"yyyy-MM-dd HH:mm:ss"
|
leftIcon={isTemporary ? <FontAwesomeIcon icon={faClock} /> : undefined}
|
||||||
)}`}
|
rightIcon={<FontAwesomeIcon icon={faCaretDown} className="ml-2" />}
|
||||||
</Button>
|
isDisabled={isMemberEditDisabled}
|
||||||
|
className={twMerge(
|
||||||
|
"border-none bg-mineshaft-600 py-2.5 text-xs capitalize hover:bg-mineshaft-500",
|
||||||
|
isTemporary && "text-primary",
|
||||||
|
isExpired && "text-red-600"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{temporaryAccess?.isTemporary
|
||||||
|
? isExpired
|
||||||
|
? "Access Expired"
|
||||||
|
: formatDistance(
|
||||||
|
new Date(temporaryAccess.temporaryAccessEndTime || ""),
|
||||||
|
new Date()
|
||||||
|
)
|
||||||
|
: "Permanent"}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent
|
<PopoverContent
|
||||||
arrowClassName="fill-gray-600"
|
arrowClassName="fill-gray-600"
|
||||||
@@ -279,7 +298,7 @@ export const IdentityRbacSection = ({ identityProjectMember, onOpenUpgradeModal
|
|||||||
</Popover>
|
</Popover>
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
className="border border-mineshaft-500 bg-mineshaft-600 hover:bg-red/20 hover:border-red/70 py-3"
|
className="border border-mineshaft-500 bg-mineshaft-600 py-3 hover:border-red/70 hover:bg-red/20"
|
||||||
ariaLabel="delete-role"
|
ariaLabel="delete-role"
|
||||||
isDisabled={isMemberEditDisabled}
|
isDisabled={isMemberEditDisabled}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -324,7 +343,7 @@ export const IdentityRbacSection = ({ identityProjectMember, onOpenUpgradeModal
|
|||||||
)}
|
)}
|
||||||
isLoading={roleForm.formState.isSubmitting}
|
isLoading={roleForm.formState.isSubmitting}
|
||||||
>
|
>
|
||||||
Save Roles
|
Save Roles
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+46
-29
@@ -1,8 +1,15 @@
|
|||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { faArrowRotateLeft, faCaretDown, faCheck, faClock, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons";
|
import {
|
||||||
|
faArrowRotateLeft,
|
||||||
|
faCaretDown,
|
||||||
|
faCheck,
|
||||||
|
faClock,
|
||||||
|
faPlus,
|
||||||
|
faTrash
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { format } from "date-fns";
|
import { format, formatDistance } from "date-fns";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -173,10 +180,15 @@ const SpecificPrivilegeSecretForm = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAccessLabel = () => {
|
const getAccessLabel = (exactTime = false) => {
|
||||||
if (isExpired) return "Access expired";
|
if (isExpired) return "Access expired";
|
||||||
if (!temporaryAccessField?.isTemporary) return "Permanent";
|
if (!temporaryAccessField?.isTemporary) return "Permanent";
|
||||||
return `Until ${format(new Date(temporaryAccessField.temporaryAccessEndTime || ""), "yyyy-MM-dd HH:mm:ss")}`;
|
if (exactTime)
|
||||||
|
return `Until ${format(
|
||||||
|
new Date(temporaryAccessField.temporaryAccessEndTime || ""),
|
||||||
|
"yyyy-MM-dd HH:mm:ss"
|
||||||
|
)}`;
|
||||||
|
return formatDistance(new Date(temporaryAccessField.temporaryAccessEndTime || ""), new Date());
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -281,24 +293,23 @@ const SpecificPrivilegeSecretForm = ({
|
|||||||
<div className="mt-7 flex items-center space-x-2">
|
<div className="mt-7 flex items-center space-x-2">
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger disabled={isMemberEditDisabled}>
|
<PopoverTrigger disabled={isMemberEditDisabled}>
|
||||||
<Tooltip
|
<div>
|
||||||
asChild
|
<Tooltip content={getAccessLabel(true)}>
|
||||||
content={isExpired ? "Timed access expired" : "Grant timed access"}
|
<Button
|
||||||
>
|
variant="outline_bg"
|
||||||
<Button
|
leftIcon={isTemporary ? <FontAwesomeIcon icon={faClock} /> : undefined}
|
||||||
variant="outline_bg"
|
rightIcon={<FontAwesomeIcon icon={faCaretDown} className="ml-2" />}
|
||||||
isDisabled={isMemberEditDisabled}
|
isDisabled={isMemberEditDisabled}
|
||||||
leftIcon={isTemporary ? <FontAwesomeIcon icon={faClock} /> : undefined}
|
className={twMerge(
|
||||||
rightIcon={<FontAwesomeIcon icon={faCaretDown} className="ml-2" />}
|
"border-none bg-mineshaft-600 py-2.5 text-xs capitalize hover:bg-mineshaft-500",
|
||||||
className={twMerge(
|
isTemporary && "text-primary",
|
||||||
"border-none py-2.5 capitalize text-xs hover:bg-mineshaft-500",
|
isExpired && "text-red-600"
|
||||||
isTemporary && "text-primary",
|
)}
|
||||||
isExpired && "text-red-600"
|
>
|
||||||
)}
|
{getAccessLabel()}
|
||||||
>
|
</Button>
|
||||||
{getAccessLabel()}
|
</Tooltip>
|
||||||
</Button>
|
</div>
|
||||||
</Tooltip>
|
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent
|
<PopoverContent
|
||||||
arrowClassName="fill-gray-600"
|
arrowClassName="fill-gray-600"
|
||||||
@@ -381,7 +392,7 @@ const SpecificPrivilegeSecretForm = ({
|
|||||||
<Tooltip content="Cancel" className="mr-4">
|
<Tooltip content="Cancel" className="mr-4">
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
className="border border-mineshaft-500 bg-mineshaft-600 hover:bg-red/20 hover:border-red/70 py-2.5"
|
className="border border-mineshaft-500 bg-mineshaft-600 py-2.5 hover:border-red/70 hover:bg-red/20"
|
||||||
ariaLabel="delete-privilege"
|
ariaLabel="delete-privilege"
|
||||||
isDisabled={privilegeForm.formState.isSubmitting}
|
isDisabled={privilegeForm.formState.isSubmitting}
|
||||||
onClick={() => privilegeForm.reset()}
|
onClick={() => privilegeForm.reset()}
|
||||||
@@ -389,7 +400,10 @@ const SpecificPrivilegeSecretForm = ({
|
|||||||
<FontAwesomeIcon icon={faArrowRotateLeft} className="py-0.5" />
|
<FontAwesomeIcon icon={faArrowRotateLeft} className="py-0.5" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip content={isMemberEditDisabled ? "Access restricted" : "Save"} className="mr-4">
|
<Tooltip
|
||||||
|
content={isMemberEditDisabled ? "Access restricted" : "Save"}
|
||||||
|
className="mr-4"
|
||||||
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
isDisabled={isMemberEditDisabled}
|
isDisabled={isMemberEditDisabled}
|
||||||
className="border-none py-3"
|
className="border-none py-3"
|
||||||
@@ -397,19 +411,22 @@ const SpecificPrivilegeSecretForm = ({
|
|||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
{privilegeForm.formState.isSubmitting ? (
|
{privilegeForm.formState.isSubmitting ? (
|
||||||
<Spinner size="xs" className="m-0 text-slate-500 w-3 h-3" />
|
<Spinner size="xs" className="m-0 h-3 w-3 text-slate-500" />
|
||||||
) : (
|
) : (
|
||||||
<FontAwesomeIcon icon={faCheck} className="px-0.5"/>
|
<FontAwesomeIcon icon={faCheck} className="px-0.5" />
|
||||||
)}
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Tooltip content={isMemberEditDisabled ? "Access restricted" : "Delete"} className="mr-4">
|
<Tooltip
|
||||||
|
content={isMemberEditDisabled ? "Access restricted" : "Delete"}
|
||||||
|
className="mr-4"
|
||||||
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
isDisabled={isMemberEditDisabled}
|
isDisabled={isMemberEditDisabled}
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
className="border border-mineshaft-500 bg-mineshaft-600 hover:bg-red/20 hover:border-red/70 py-3"
|
className="border border-mineshaft-500 bg-mineshaft-600 py-3 hover:border-red/70 hover:bg-red/20"
|
||||||
ariaLabel="delete-privilege"
|
ariaLabel="delete-privilege"
|
||||||
onClick={() => handlePopUpOpen("deletePrivilege")}
|
onClick={() => handlePopUpOpen("deletePrivilege")}
|
||||||
>
|
>
|
||||||
@@ -480,7 +497,7 @@ export const SpecificPrivilegeSection = ({ identityId }: Props) => {
|
|||||||
Additional Privileges
|
Additional Privileges
|
||||||
{isLoading && <Spinner size="xs" />}
|
{isLoading && <Spinner size="xs" />}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-mineshaft-400 mt-0.5">
|
<p className="mt-0.5 text-sm text-mineshaft-400">
|
||||||
Select individual privileges to associate with the identity.
|
Select individual privileges to associate with the identity.
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ const MAX_ROLES_TO_BE_SHOWN_IN_TABLE = 2;
|
|||||||
const formatRoleName = (role: string, customRoleName?: string) => {
|
const formatRoleName = (role: string, customRoleName?: string) => {
|
||||||
if (role === ProjectMembershipRole.Custom) return customRoleName;
|
if (role === ProjectMembershipRole.Custom) return customRoleName;
|
||||||
if (role === ProjectMembershipRole.Member) return "Developer";
|
if (role === ProjectMembershipRole.Member) return "Developer";
|
||||||
|
if (role === ProjectMembershipRole.NoAccess) return "No access";
|
||||||
return role;
|
return role;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -279,7 +280,9 @@ export const MemberListTab = () => {
|
|||||||
return (
|
return (
|
||||||
<Tag key={id}>
|
<Tag key={id}>
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<div>{formatRoleName(role, customRoleName)}</div>
|
<div className="capitalize">
|
||||||
|
{formatRoleName(role, customRoleName)}
|
||||||
|
</div>
|
||||||
{isTemporary && (
|
{isTemporary && (
|
||||||
<div>
|
<div>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
|||||||
+43
-24
@@ -1,8 +1,9 @@
|
|||||||
|
/* eslint-disable no-nested-ternary */
|
||||||
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
import { Controller, useFieldArray, useForm } from "react-hook-form";
|
||||||
import { faCaretDown, faClock, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons";
|
import { faCaretDown, faClock, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { format } from "date-fns";
|
import { format, formatDistance } from "date-fns";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -21,7 +22,8 @@ import {
|
|||||||
Select,
|
Select,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
Spinner,
|
Spinner,
|
||||||
Tag
|
Tag,
|
||||||
|
Tooltip
|
||||||
} from "@app/components/v2";
|
} from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionActions,
|
ProjectPermissionActions,
|
||||||
@@ -170,7 +172,7 @@ export const MemberRbacSection = ({ projectMember, onOpenUpgradeModal }: Props)
|
|||||||
{...field}
|
{...field}
|
||||||
isDisabled={isMemberEditDisabled}
|
isDisabled={isMemberEditDisabled}
|
||||||
onValueChange={(e) => onChange(e)}
|
onValueChange={(e) => onChange(e)}
|
||||||
className="w-full bg-mineshaft-600 hover:bg-mineshaft-500 duration-200"
|
className="w-full bg-mineshaft-600 duration-200 hover:bg-mineshaft-500"
|
||||||
>
|
>
|
||||||
{projectRoles?.map(({ name, slug, id: projectRoleId }) => (
|
{projectRoles?.map(({ name, slug, id: projectRoleId }) => (
|
||||||
<SelectItem value={slug} key={projectRoleId}>
|
<SelectItem value={slug} key={projectRoleId}>
|
||||||
@@ -181,25 +183,42 @@ export const MemberRbacSection = ({ projectMember, onOpenUpgradeModal }: Props)
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger disabled={isMemberEditDisabled}>
|
<PopoverTrigger disabled={isMemberEditDisabled} asChild>
|
||||||
<Button
|
<div>
|
||||||
variant="outline_bg"
|
<Tooltip
|
||||||
leftIcon={isTemporary ? <FontAwesomeIcon icon={faClock} /> : undefined}
|
content={
|
||||||
rightIcon={<FontAwesomeIcon icon={faCaretDown} className="ml-2" />}
|
temporaryAccess?.isTemporary
|
||||||
isDisabled={isMemberEditDisabled}
|
? isExpired
|
||||||
className={twMerge(
|
? "Timed Access Expired"
|
||||||
"border-none bg-mineshaft-600 hover:bg-mineshaft-500 py-2.5 capitalize text-xs",
|
: `Until ${format(
|
||||||
isTemporary && "text-primary",
|
new Date(temporaryAccess.temporaryAccessEndTime || ""),
|
||||||
isExpired && "text-red-600"
|
"yyyy-MM-dd HH:mm:ss"
|
||||||
)}
|
)}`
|
||||||
>
|
: "Non expiry access"
|
||||||
{!temporaryAccess?.isTemporary
|
}
|
||||||
? "Permanent"
|
>
|
||||||
: `Until ${format(
|
<Button
|
||||||
new Date(temporaryAccess.temporaryAccessEndTime || ""),
|
variant="outline_bg"
|
||||||
"yyyy-MM-dd HH:mm:ss"
|
leftIcon={isTemporary ? <FontAwesomeIcon icon={faClock} /> : undefined}
|
||||||
)}`}
|
rightIcon={<FontAwesomeIcon icon={faCaretDown} className="ml-2" />}
|
||||||
</Button>
|
isDisabled={isMemberEditDisabled}
|
||||||
|
className={twMerge(
|
||||||
|
"border-none bg-mineshaft-600 py-2.5 text-xs capitalize hover:bg-mineshaft-500",
|
||||||
|
isTemporary && "text-primary",
|
||||||
|
isExpired && "text-red-600"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{temporaryAccess?.isTemporary
|
||||||
|
? isExpired
|
||||||
|
? "Access Expired"
|
||||||
|
: formatDistance(
|
||||||
|
new Date(temporaryAccess.temporaryAccessEndTime || ""),
|
||||||
|
new Date()
|
||||||
|
)
|
||||||
|
: "Permanent"}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent
|
<PopoverContent
|
||||||
arrowClassName="fill-gray-600"
|
arrowClassName="fill-gray-600"
|
||||||
@@ -279,7 +298,7 @@ export const MemberRbacSection = ({ projectMember, onOpenUpgradeModal }: Props)
|
|||||||
</Popover>
|
</Popover>
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
className="border border-mineshaft-500 bg-mineshaft-600 hover:bg-red/20 hover:border-red/70 py-3"
|
className="border border-mineshaft-500 bg-mineshaft-600 py-3 hover:border-red/70 hover:bg-red/20"
|
||||||
ariaLabel="delete-role"
|
ariaLabel="delete-role"
|
||||||
isDisabled={isMemberEditDisabled}
|
isDisabled={isMemberEditDisabled}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -321,7 +340,7 @@ export const MemberRbacSection = ({ projectMember, onOpenUpgradeModal }: Props)
|
|||||||
)}
|
)}
|
||||||
isLoading={roleForm.formState.isSubmitting}
|
isLoading={roleForm.formState.isSubmitting}
|
||||||
>
|
>
|
||||||
Save Roles
|
Save Roles
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+46
-29
@@ -1,8 +1,15 @@
|
|||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
import { faArrowRotateLeft, faCaretDown, faCheck, faClock, faPlus, faTrash } from "@fortawesome/free-solid-svg-icons";
|
import {
|
||||||
|
faArrowRotateLeft,
|
||||||
|
faCaretDown,
|
||||||
|
faCheck,
|
||||||
|
faClock,
|
||||||
|
faPlus,
|
||||||
|
faTrash
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { format } from "date-fns";
|
import { format, formatDistance } from "date-fns";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
import { twMerge } from "tailwind-merge";
|
import { twMerge } from "tailwind-merge";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -161,10 +168,15 @@ const SpecificPrivilegeSecretForm = ({ privilege }: { privilege: TProjectUserPri
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAccessLabel = () => {
|
const getAccessLabel = (exactTime = false) => {
|
||||||
if (isExpired) return "Access expired";
|
if (isExpired) return "Access expired";
|
||||||
if (!temporaryAccessField?.isTemporary) return "Permanent";
|
if (!temporaryAccessField?.isTemporary) return "Permanent";
|
||||||
return `Until ${format(new Date(temporaryAccessField.temporaryAccessEndTime || ""), "yyyy-MM-dd HH:mm:ss")}`;
|
if (exactTime)
|
||||||
|
return `Until ${format(
|
||||||
|
new Date(temporaryAccessField.temporaryAccessEndTime || ""),
|
||||||
|
"yyyy-MM-dd HH:mm:ss"
|
||||||
|
)}`;
|
||||||
|
return formatDistance(new Date(temporaryAccessField.temporaryAccessEndTime || ""), new Date());
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -269,24 +281,23 @@ const SpecificPrivilegeSecretForm = ({ privilege }: { privilege: TProjectUserPri
|
|||||||
<div className="mt-7 flex items-center space-x-2">
|
<div className="mt-7 flex items-center space-x-2">
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger disabled={isMemberEditDisabled}>
|
<PopoverTrigger disabled={isMemberEditDisabled}>
|
||||||
<Tooltip
|
<div>
|
||||||
asChild
|
<Tooltip content={getAccessLabel(true)}>
|
||||||
content={isExpired ? "Timed access expired" : "Grant timed access"}
|
<Button
|
||||||
>
|
variant="outline_bg"
|
||||||
<Button
|
leftIcon={isTemporary ? <FontAwesomeIcon icon={faClock} /> : undefined}
|
||||||
variant="outline_bg"
|
rightIcon={<FontAwesomeIcon icon={faCaretDown} className="ml-2" />}
|
||||||
isDisabled={isMemberEditDisabled}
|
isDisabled={isMemberEditDisabled}
|
||||||
leftIcon={isTemporary ? <FontAwesomeIcon icon={faClock} /> : undefined}
|
className={twMerge(
|
||||||
rightIcon={<FontAwesomeIcon icon={faCaretDown} className="ml-2" />}
|
"border-none bg-mineshaft-600 py-2.5 text-xs capitalize hover:bg-mineshaft-500",
|
||||||
className={twMerge(
|
isTemporary && "text-primary",
|
||||||
"border-none py-2.5 capitalize text-xs hover:bg-mineshaft-500",
|
isExpired && "text-red-600"
|
||||||
isTemporary && "text-primary",
|
)}
|
||||||
isExpired && "text-red-600"
|
>
|
||||||
)}
|
{getAccessLabel()}
|
||||||
>
|
</Button>
|
||||||
{getAccessLabel()}
|
</Tooltip>
|
||||||
</Button>
|
</div>
|
||||||
</Tooltip>
|
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent
|
<PopoverContent
|
||||||
arrowClassName="fill-gray-600"
|
arrowClassName="fill-gray-600"
|
||||||
@@ -369,7 +380,7 @@ const SpecificPrivilegeSecretForm = ({ privilege }: { privilege: TProjectUserPri
|
|||||||
<Tooltip content="Cancel" className="mr-4">
|
<Tooltip content="Cancel" className="mr-4">
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
className="border border-mineshaft-500 bg-mineshaft-600 hover:bg-red/20 hover:border-red/70 py-2.5"
|
className="border border-mineshaft-500 bg-mineshaft-600 py-2.5 hover:border-red/70 hover:bg-red/20"
|
||||||
ariaLabel="delete-privilege"
|
ariaLabel="delete-privilege"
|
||||||
isDisabled={privilegeForm.formState.isSubmitting}
|
isDisabled={privilegeForm.formState.isSubmitting}
|
||||||
onClick={() => privilegeForm.reset()}
|
onClick={() => privilegeForm.reset()}
|
||||||
@@ -377,7 +388,10 @@ const SpecificPrivilegeSecretForm = ({ privilege }: { privilege: TProjectUserPri
|
|||||||
<FontAwesomeIcon icon={faArrowRotateLeft} className="py-0.5" />
|
<FontAwesomeIcon icon={faArrowRotateLeft} className="py-0.5" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip content={isMemberEditDisabled ? "Access restricted" : "Save"} className="mr-4">
|
<Tooltip
|
||||||
|
content={isMemberEditDisabled ? "Access restricted" : "Save"}
|
||||||
|
className="mr-4"
|
||||||
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
isDisabled={isMemberEditDisabled}
|
isDisabled={isMemberEditDisabled}
|
||||||
className="border-none py-3"
|
className="border-none py-3"
|
||||||
@@ -385,19 +399,22 @@ const SpecificPrivilegeSecretForm = ({ privilege }: { privilege: TProjectUserPri
|
|||||||
type="submit"
|
type="submit"
|
||||||
>
|
>
|
||||||
{privilegeForm.formState.isSubmitting ? (
|
{privilegeForm.formState.isSubmitting ? (
|
||||||
<Spinner size="xs" className="m-0 text-slate-500 w-3 h-3" />
|
<Spinner size="xs" className="m-0 h-3 w-3 text-slate-500" />
|
||||||
) : (
|
) : (
|
||||||
<FontAwesomeIcon icon={faCheck} className="px-0.5"/>
|
<FontAwesomeIcon icon={faCheck} className="px-0.5" />
|
||||||
)}
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Tooltip content={isMemberEditDisabled ? "Access restricted" : "Delete"} className="mr-4">
|
<Tooltip
|
||||||
|
content={isMemberEditDisabled ? "Access restricted" : "Delete"}
|
||||||
|
className="mr-4"
|
||||||
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
isDisabled={isMemberEditDisabled}
|
isDisabled={isMemberEditDisabled}
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
className="border border-mineshaft-500 bg-mineshaft-600 hover:bg-red/20 hover:border-red/70 py-3"
|
className="border border-mineshaft-500 bg-mineshaft-600 py-3 hover:border-red/70 hover:bg-red/20"
|
||||||
ariaLabel="delete-privilege"
|
ariaLabel="delete-privilege"
|
||||||
onClick={() => handlePopUpOpen("deletePrivilege")}
|
onClick={() => handlePopUpOpen("deletePrivilege")}
|
||||||
>
|
>
|
||||||
@@ -463,7 +480,7 @@ export const SpecificPrivilegeSection = ({ membershipId }: Props) => {
|
|||||||
Additional Privileges
|
Additional Privileges
|
||||||
{isLoading && <Spinner size="xs" />}
|
{isLoading && <Spinner size="xs" />}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-mineshaft-400 mt-0.5">
|
<p className="mt-0.5 text-sm text-mineshaft-400">
|
||||||
Select individual privileges to associate with the user.
|
Select individual privileges to associate with the user.
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user