diff --git a/apiserver/plane/app/serializers/api.py b/apiserver/plane/app/serializers/api.py index 264a58f92..009f7a611 100644 --- a/apiserver/plane/app/serializers/api.py +++ b/apiserver/plane/app/serializers/api.py @@ -1,5 +1,7 @@ from .base import BaseSerializer from plane.db.models import APIToken, APIActivityLog +from rest_framework import serializers +from django.utils import timezone class APITokenSerializer(BaseSerializer): @@ -17,10 +19,17 @@ class APITokenSerializer(BaseSerializer): class APITokenReadSerializer(BaseSerializer): + is_active = serializers.SerializerMethodField() + class Meta: model = APIToken exclude = ("token",) + def get_is_active(self, obj: APIToken) -> bool: + if obj.expired_at is None: + return True + return timezone.now() < obj.expired_at + class APIActivityLogSerializer(BaseSerializer): class Meta: diff --git a/web/core/components/api-token/modal/create-token-modal.tsx b/web/core/components/api-token/modal/create-token-modal.tsx index 62c245605..eb6d8220f 100644 --- a/web/core/components/api-token/modal/create-token-modal.tsx +++ b/web/core/components/api-token/modal/create-token-modal.tsx @@ -46,7 +46,7 @@ export const CreateApiTokenModal: React.FC = (props) => { const csvData = { Title: data.label, Description: data.description, - Expiry: data.expired_at ? renderFormattedDate(data.expired_at)?.replace(",", " ") ?? "" : "Never expires", + Expiry: data.expired_at ? (renderFormattedDate(data.expired_at)?.replace(",", " ") ?? "") : "Never expires", "Secret key": data.token ?? "", }; diff --git a/web/core/components/api-token/modal/form.tsx b/web/core/components/api-token/modal/form.tsx index 17e14ae56..8bee88d9f 100644 --- a/web/core/components/api-token/modal/form.tsx +++ b/web/core/components/api-token/modal/form.tsx @@ -13,7 +13,7 @@ import { Button, CustomSelect, Input, TextArea, ToggleSwitch, TOAST_TYPE, setToa import { DateDropdown } from "@/components/dropdowns"; // helpers import { cn } from "@/helpers/common.helper"; -import { renderFormattedDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper"; +import { renderFormattedDate, renderFormattedTime } from "@/helpers/date-time.helper"; type Props = { handleClose: () => void; @@ -51,20 +51,21 @@ const defaultValues: Partial = { expired_at: null, }; -const getExpiryDate = (val: string): string | null | undefined => { +const getExpiryDate = (val: string): Date | null | undefined => { const today = new Date(); - const dateToAdd = EXPIRY_DATE_OPTIONS.find((option) => option.key === val)?.value; - - if (dateToAdd) { - const expiryDate = add(today, dateToAdd); - - return renderFormattedDate(expiryDate); - } - + if (dateToAdd) return add(today, dateToAdd); return null; }; +const getFormattedDate = (date: Date): Date => { + const now = new Date(); + const hours = now.getHours(); + const minutes = now.getMinutes(); + const seconds = now.getSeconds(); + return add(date, { hours, minutes, seconds }); +}; + export const CreateApiTokenForm: React.FC = (props) => { const { handleClose, neverExpires, toggleNeverExpires, onSubmit } = props; // states @@ -97,12 +98,13 @@ export const CreateApiTokenForm: React.FC = (props) => { // if never expires is toggled on, set expired_at to null if (neverExpires) payload.expired_at = null; // if never expires is toggled off, and the user has selected a custom date, set expired_at to the custom date - else if (data.expired_at === "custom") payload.expired_at = renderFormattedPayloadDate(customDate); + else if (data.expired_at === "custom") { + payload.expired_at = customDate && getFormattedDate(customDate).toISOString(); + } // if never expires is toggled off, and the user has selected a predefined date, set expired_at to the predefined date else { const expiryDate = getExpiryDate(data.expired_at ?? ""); - - if (expiryDate) payload.expired_at = renderFormattedPayloadDate(new Date(expiryDate)); + if (expiryDate) payload.expired_at = expiryDate.toISOString(); } await onSubmit(payload).then(() => { @@ -114,6 +116,8 @@ export const CreateApiTokenForm: React.FC = (props) => { const today = new Date(); const tomorrow = add(today, { days: 1 }); const expiredAt = watch("expired_at"); + const expiryDate = getExpiryDate(expiredAt ?? ""); + const customDateFormatted = customDate && getFormattedDate(customDate); return (
@@ -219,10 +223,10 @@ export const CreateApiTokenForm: React.FC = (props) => { {expiredAt === "custom" ? customDate - ? `Expires ${renderFormattedDate(customDate)}` + ? `Expires ${renderFormattedDate(customDateFormatted ?? "")} at ${renderFormattedTime(customDateFormatted ?? "")}` : null : expiredAt - ? `Expires ${getExpiryDate(expiredAt ?? "")}` + ? `Expires ${renderFormattedDate(expiryDate ?? "")} at ${renderFormattedTime(expiryDate ?? "")}` : null} )} @@ -249,4 +253,4 @@ export const CreateApiTokenForm: React.FC = (props) => { ); -}; +}; \ No newline at end of file diff --git a/web/core/components/api-token/modal/generated-token-details.tsx b/web/core/components/api-token/modal/generated-token-details.tsx index 11444e023..e6e3d35fe 100644 --- a/web/core/components/api-token/modal/generated-token-details.tsx +++ b/web/core/components/api-token/modal/generated-token-details.tsx @@ -6,7 +6,7 @@ import { IApiToken } from "@plane/types"; // ui import { Button, Tooltip, TOAST_TYPE, setToast } from "@plane/ui"; // helpers -import { renderFormattedDate } from "@/helpers/date-time.helper"; +import { renderFormattedDate, renderFormattedTime } from "@/helpers/date-time.helper"; import { copyTextToClipboard } from "@/helpers/string.helper"; // types import { usePlatformOS } from "@/hooks/use-platform-os"; @@ -49,7 +49,9 @@ export const GeneratedTokenDetails: React.FC = (props) => {

- {tokenDetails.expired_at ? `Expires ${renderFormattedDate(tokenDetails.expired_at)}` : "Never expires"} + {tokenDetails.expired_at + ? `Expires ${renderFormattedDate(tokenDetails.expired_at!)} at ${renderFormattedTime(tokenDetails.expired_at!)}` + : "Never expires"}

); -}; +}; \ No newline at end of file