Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f8a5095f14 | |||
| 1a462711e1 | |||
| aa3702cd46 | |||
| aed87ef472 | |||
| aaad37575b | |||
| 42b524b16a | |||
| 0bc4b6cece | |||
| 43c75f4457 |
@@ -2,27 +2,6 @@ name: Branch Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build-web:
|
||||
required: false
|
||||
description: "Build Web"
|
||||
type: boolean
|
||||
default: false
|
||||
build-space:
|
||||
required: false
|
||||
description: "Build Space"
|
||||
type: boolean
|
||||
default: false
|
||||
build-api:
|
||||
required: false
|
||||
description: "Build API"
|
||||
type: boolean
|
||||
default: false
|
||||
build-proxy:
|
||||
required: false
|
||||
description: "Build Proxy"
|
||||
type: boolean
|
||||
default: false
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
@@ -95,7 +74,7 @@ jobs:
|
||||
- nginx/**
|
||||
|
||||
branch_build_push_frontend:
|
||||
if: ${{ needs.branch_build_setup.outputs.build_frontend == 'true' || github.event.inputs.build-web=='true' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }}
|
||||
if: ${{ needs.branch_build_setup.outputs.build_frontend == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }}
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [branch_build_setup]
|
||||
env:
|
||||
@@ -147,7 +126,7 @@ jobs:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
branch_build_push_space:
|
||||
if: ${{ needs.branch_build_setup.outputs.build_space == 'true' || github.event.inputs.build-space=='true' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }}
|
||||
if: ${{ needs.branch_build_setup.outputs.build_space == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }}
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [branch_build_setup]
|
||||
env:
|
||||
@@ -199,7 +178,7 @@ jobs:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
branch_build_push_backend:
|
||||
if: ${{ needs.branch_build_setup.outputs.build_backend == 'true' || github.event.inputs.build-api=='true' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }}
|
||||
if: ${{ needs.branch_build_setup.outputs.build_backend == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }}
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [branch_build_setup]
|
||||
env:
|
||||
@@ -251,7 +230,7 @@ jobs:
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
branch_build_push_proxy:
|
||||
if: ${{ needs.branch_build_setup.outputs.build_proxy == 'true' || github.event.inputs.build-web=='true' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }}
|
||||
if: ${{ needs.branch_build_setup.outputs.build_proxy == 'true' || github.event_name == 'workflow_dispatch' || github.event_name == 'release' || needs.branch_build_setup.outputs.gh_branch_name == 'master' }}
|
||||
runs-on: ubuntu-20.04
|
||||
needs: [branch_build_setup]
|
||||
env:
|
||||
|
||||
@@ -2,12 +2,11 @@ name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ 'develop', 'preview', 'master' ]
|
||||
branches: ["master"]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ 'develop', 'preview', 'master' ]
|
||||
branches: ["develop", "preview", "master"]
|
||||
schedule:
|
||||
- cron: '53 19 * * 5'
|
||||
- cron: "53 19 * * 5"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
@@ -21,45 +20,44 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'python', 'javascript' ]
|
||||
language: ["python", "javascript"]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Use only 'java' to analyze code written in Java, Kotlin or both
|
||||
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
@@ -88,7 +88,10 @@ class ProjectViewSet(WebhookMixin, BaseViewSet):
|
||||
.get_queryset()
|
||||
.filter(workspace__slug=self.kwargs.get("slug"))
|
||||
.filter(
|
||||
Q(project_projectmember__member=self.request.user)
|
||||
Q(
|
||||
project_projectmember__member=self.request.user,
|
||||
project_projectmember__is_active=True,
|
||||
)
|
||||
| Q(network=2)
|
||||
)
|
||||
.select_related(
|
||||
@@ -173,10 +176,7 @@ class ProjectViewSet(WebhookMixin, BaseViewSet):
|
||||
for field in request.GET.get("fields", "").split(",")
|
||||
if field
|
||||
]
|
||||
projects = (
|
||||
self.get_queryset()
|
||||
.order_by("sort_order", "name")
|
||||
)
|
||||
projects = self.get_queryset().order_by("sort_order", "name")
|
||||
if request.GET.get("per_page", False) and request.GET.get(
|
||||
"cursor", False
|
||||
):
|
||||
@@ -576,9 +576,11 @@ class ProjectJoinEndpoint(BaseAPIView):
|
||||
_ = WorkspaceMember.objects.create(
|
||||
workspace_id=project_invite.workspace_id,
|
||||
member=user,
|
||||
role=15
|
||||
if project_invite.role >= 15
|
||||
else project_invite.role,
|
||||
role=(
|
||||
15
|
||||
if project_invite.role >= 15
|
||||
else project_invite.role
|
||||
),
|
||||
)
|
||||
else:
|
||||
# Else make him active
|
||||
@@ -685,9 +687,14 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||
)
|
||||
|
||||
bulk_project_members = []
|
||||
member_roles = {member.get("member_id"): member.get("role") for member in members}
|
||||
member_roles = {
|
||||
member.get("member_id"): member.get("role") for member in members
|
||||
}
|
||||
# Update roles in the members array based on the member_roles dictionary
|
||||
for project_member in ProjectMember.objects.filter(project_id=project_id, member_id__in=[member.get("member_id") for member in members]):
|
||||
for project_member in ProjectMember.objects.filter(
|
||||
project_id=project_id,
|
||||
member_id__in=[member.get("member_id") for member in members],
|
||||
):
|
||||
project_member.role = member_roles[str(project_member.member_id)]
|
||||
project_member.is_active = True
|
||||
bulk_project_members.append(project_member)
|
||||
@@ -710,9 +717,9 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||
role=member.get("role", 10),
|
||||
project_id=project_id,
|
||||
workspace_id=project.workspace_id,
|
||||
sort_order=sort_order[0] - 10000
|
||||
if len(sort_order)
|
||||
else 65535,
|
||||
sort_order=(
|
||||
sort_order[0] - 10000 if len(sort_order) else 65535
|
||||
),
|
||||
)
|
||||
)
|
||||
bulk_issue_props.append(
|
||||
@@ -733,7 +740,10 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||
bulk_issue_props, batch_size=10, ignore_conflicts=True
|
||||
)
|
||||
|
||||
project_members = ProjectMember.objects.filter(project_id=project_id, member_id__in=[member.get("member_id") for member in members])
|
||||
project_members = ProjectMember.objects.filter(
|
||||
project_id=project_id,
|
||||
member_id__in=[member.get("member_id") for member in members],
|
||||
)
|
||||
serializer = ProjectMemberRoleSerializer(project_members, many=True)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import { AlertLabel } from "src/ui/components/alert-label";
|
||||
import { IVerticalDropdownItemProps, VerticalDropdownMenu } from "src/ui/components/vertical-dropdown-menu";
|
||||
import { SummaryPopover } from "src/ui/components/summary-popover";
|
||||
import { InfoPopover } from "src/ui/components/info-popover";
|
||||
import { getDate } from "src/utils/date-utils";
|
||||
|
||||
interface IEditorHeader {
|
||||
editor: Editor;
|
||||
@@ -72,7 +73,7 @@ export const EditorHeader = (props: IEditorHeader) => {
|
||||
Icon={Archive}
|
||||
backgroundColor="bg-blue-500/20"
|
||||
textColor="text-blue-500"
|
||||
label={`Archived at ${new Date(archivedAt).toLocaleString()}`}
|
||||
label={`Archived at ${getDate(archivedAt)?.toLocaleString()}`}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -3,13 +3,15 @@ import { usePopper } from "react-popper";
|
||||
import { Calendar, History, Info } from "lucide-react";
|
||||
// types
|
||||
import { DocumentDetails } from "src/types/editor-types";
|
||||
//utils
|
||||
import { getDate } from "src/utils/date-utils";
|
||||
|
||||
type Props = {
|
||||
documentDetails: DocumentDetails;
|
||||
};
|
||||
|
||||
// function to render a Date in the format- 25 May 2023 at 2:53PM
|
||||
const renderDate = (date: Date): string => {
|
||||
const renderDate = (date: Date | undefined): string => {
|
||||
const options: Intl.DateTimeFormatOptions = {
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
@@ -52,14 +54,14 @@ export const InfoPopover: React.FC<Props> = (props) => {
|
||||
<h6 className="text-xs text-custom-text-400">Last updated on</h6>
|
||||
<h5 className="flex items-center gap-1 text-sm">
|
||||
<History className="h-3 w-3" />
|
||||
{renderDate(new Date(documentDetails.last_updated_at))}
|
||||
{renderDate(getDate(documentDetails?.last_updated_at))}
|
||||
</h5>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<h6 className="text-xs text-custom-text-400">Created on</h6>
|
||||
<h5 className="flex items-center gap-1 text-sm">
|
||||
<Calendar className="h-3 w-3" />
|
||||
{renderDate(new Date(documentDetails.created_on))}
|
||||
{renderDate(getDate(documentDetails?.created_on))}
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
function isNumber(value: any) {
|
||||
return typeof value === "number";
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a date from string of type yyyy-mm-dd
|
||||
* This method is recommended to use instead of new Date() as this does not introduce any timezone offsets
|
||||
* @param date
|
||||
* @returns date or undefined
|
||||
*/
|
||||
export const getDate = (date: string | Date | undefined | null): Date | undefined => {
|
||||
if (!date || date === "") return;
|
||||
|
||||
if (typeof date !== "string" && !(date instanceof String)) return date;
|
||||
const [yearString, monthString, dayString] = date.substring(0, 10).split("-");
|
||||
const year = parseInt(yearString);
|
||||
const month = parseInt(monthString);
|
||||
const day = parseInt(dayString);
|
||||
if (!isNumber(year) || !isNumber(month) || !isNumber(day)) return;
|
||||
|
||||
return new Date(year, month - 1, day);
|
||||
};
|
||||
Vendored
+2
-2
@@ -6,7 +6,7 @@ export interface IPage {
|
||||
archived_at: string | null;
|
||||
blocks: IPageBlock[];
|
||||
color: string;
|
||||
created_at: Date;
|
||||
created_at: string | null;
|
||||
created_by: string;
|
||||
description: string;
|
||||
description_html: string;
|
||||
@@ -20,7 +20,7 @@ export interface IPage {
|
||||
owned_by: string;
|
||||
project: string;
|
||||
project_detail: IProjectLite;
|
||||
updated_at: Date;
|
||||
updated_at: string | null;
|
||||
updated_by: string;
|
||||
workspace: string;
|
||||
workspace_detail: IWorkspaceLite;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { DateFilterSelect } from "./date-filter-select";
|
||||
// ui
|
||||
import { Button } from "@plane/ui";
|
||||
// helpers
|
||||
import { renderFormattedPayloadDate, renderFormattedDate } from "helpers/date-time.helper";
|
||||
import { renderFormattedPayloadDate, renderFormattedDate, getDate } from "helpers/date-time.helper";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
@@ -43,7 +43,10 @@ export const DateFilterModal: React.FC<Props> = ({ title, handleClose, isOpen, o
|
||||
handleClose();
|
||||
};
|
||||
|
||||
const isInvalid = watch("filterType") === "range" ? new Date(watch("date1")) > new Date(watch("date2")) : false;
|
||||
const date1 = getDate(watch("date1"));
|
||||
const date2 = getDate(watch("date1"));
|
||||
|
||||
const isInvalid = watch("filterType") === "range" && date1 && date2 ? date1 > date2 : false;
|
||||
|
||||
return (
|
||||
<Transition.Root show={isOpen} as={Fragment}>
|
||||
@@ -86,35 +89,45 @@ export const DateFilterModal: React.FC<Props> = ({ title, handleClose, isOpen, o
|
||||
<Controller
|
||||
control={control}
|
||||
name="date1"
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<DayPicker
|
||||
selected={value ? new Date(value) : undefined}
|
||||
defaultMonth={value ? new Date(value) : undefined}
|
||||
onSelect={(date) => onChange(date)}
|
||||
mode="single"
|
||||
disabled={[
|
||||
{ after: new Date(watch("date2")) }
|
||||
]}
|
||||
className="border border-custom-border-200 p-3 rounded-md"
|
||||
/>
|
||||
)}
|
||||
render={({ field: { value, onChange } }) => {
|
||||
const dateValue = getDate(value);
|
||||
const date2Value = getDate(watch("date2"));
|
||||
return (
|
||||
<DayPicker
|
||||
selected={dateValue}
|
||||
defaultMonth={dateValue}
|
||||
onSelect={(date) => {
|
||||
if (!date) return;
|
||||
onChange(date);
|
||||
}}
|
||||
mode="single"
|
||||
disabled={date2Value ? [{ after: date2Value }] : undefined}
|
||||
className="border border-custom-border-200 p-3 rounded-md"
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{watch("filterType") === "range" && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="date2"
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<DayPicker
|
||||
selected={value ? new Date(value) : undefined}
|
||||
defaultMonth={value ? new Date(value) : undefined}
|
||||
onSelect={(date) => onChange(date)}
|
||||
mode="single"
|
||||
disabled={[
|
||||
{ before: new Date(watch("date1")) }
|
||||
]}
|
||||
className="border border-custom-border-200 p-3 rounded-md"
|
||||
/>
|
||||
)}
|
||||
render={({ field: { value, onChange } }) => {
|
||||
const dateValue = getDate(value);
|
||||
const date1Value = getDate(watch("date1"));
|
||||
return (
|
||||
<DayPicker
|
||||
selected={dateValue}
|
||||
defaultMonth={dateValue}
|
||||
onSelect={(date) => {
|
||||
if (!date) return;
|
||||
onChange(date);
|
||||
}}
|
||||
mode="single"
|
||||
disabled={date1Value ? [{ before: date1Value }] : undefined}
|
||||
className="border border-custom-border-200 p-3 rounded-md"
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,8 @@ import useToast from "hooks/use-toast";
|
||||
import { usePopper } from "react-popper";
|
||||
// ui
|
||||
import { Button, Input } from "@plane/ui";
|
||||
// icons
|
||||
import { AlertCircle } from "lucide-react";
|
||||
// components
|
||||
import { RichReadOnlyEditorWithRef } from "@plane/rich-text-editor";
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
@@ -250,8 +252,17 @@ export const GptAssistantPopover: React.FC<Props> = (props) => {
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<div className={`flex gap-2 ${response === "" ? "justify-end" : "justify-between"}`}>
|
||||
{responseActionButton}
|
||||
<div className="flex gap-2 justify-between">
|
||||
{responseActionButton ? (
|
||||
<>{responseActionButton}</>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex items-center justify-center gap-2 text-sm text-custom-primary">
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<p>By using this feature, you consent to sharing the message with a 3rd party service. </p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="neutral-primary" size="sm" onClick={onClose}>
|
||||
Close
|
||||
|
||||
@@ -3,7 +3,7 @@ import { eachDayOfInterval, isValid } from "date-fns";
|
||||
// ui
|
||||
import { LineGraph } from "components/ui";
|
||||
// helpers
|
||||
import { renderFormattedDateWithoutYear } from "helpers/date-time.helper";
|
||||
import { getDate, renderFormattedDateWithoutYear } from "helpers/date-time.helper";
|
||||
//types
|
||||
import { TCompletionChartDistribution } from "@plane/types";
|
||||
|
||||
@@ -47,11 +47,11 @@ const ProgressChart: React.FC<Props> = ({ distribution, startDate, endDate, tota
|
||||
}));
|
||||
|
||||
const generateXAxisTickValues = () => {
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
const start = getDate(startDate);
|
||||
const end = getDate(endDate);
|
||||
|
||||
let dates: Date[] = [];
|
||||
if (isValid(start) && isValid(end)) {
|
||||
if (start && end && isValid(start) && isValid(end)) {
|
||||
dates = eachDayOfInterval({ start, end });
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,12 @@ import { EmptyState, getEmptyStateImagePath } from "components/empty-state";
|
||||
// icons
|
||||
import { ArrowRight, CalendarCheck, CalendarDays, Star, Target } from "lucide-react";
|
||||
// helpers
|
||||
import { renderFormattedDate, findHowManyDaysLeft, renderFormattedDateWithoutYear } from "helpers/date-time.helper";
|
||||
import {
|
||||
renderFormattedDate,
|
||||
findHowManyDaysLeft,
|
||||
renderFormattedDateWithoutYear,
|
||||
getDate,
|
||||
} from "helpers/date-time.helper";
|
||||
import { truncateText } from "helpers/string.helper";
|
||||
// types
|
||||
import { ICycle, TCycleGroups } from "@plane/types";
|
||||
@@ -102,8 +107,10 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
|
||||
/>
|
||||
);
|
||||
|
||||
const endDate = new Date(activeCycle.end_date ?? "");
|
||||
const startDate = new Date(activeCycle.start_date ?? "");
|
||||
const endDate = getDate(activeCycle.end_date);
|
||||
const startDate = getDate(activeCycle.start_date);
|
||||
const daysLeft = findHowManyDaysLeft(activeCycle.end_date) ?? 0;
|
||||
const cycleStatus = activeCycle.status.toLowerCase() as TCycleGroups;
|
||||
|
||||
const groupedIssues: any = {
|
||||
backlog: activeCycle.backlog_issues,
|
||||
@@ -113,8 +120,6 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
|
||||
cancelled: activeCycle.cancelled_issues,
|
||||
};
|
||||
|
||||
const cycleStatus = activeCycle.status.toLowerCase() as TCycleGroups;
|
||||
|
||||
const handleAddToFavorites = (e: MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
@@ -151,8 +156,6 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
|
||||
color: group.color,
|
||||
}));
|
||||
|
||||
const daysLeft = findHowManyDaysLeft(activeCycle.end_date) ?? 0;
|
||||
|
||||
return (
|
||||
<div className="grid-row-2 grid divide-y rounded-[10px] border border-custom-border-200 bg-custom-background-100 shadow">
|
||||
<div className="grid grid-cols-1 divide-y border-custom-border-200 lg:grid-cols-3 lg:divide-x lg:divide-y-0">
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Avatar, AvatarGroup, CustomMenu, Tooltip, LayersIcon, CycleGroupIcon }
|
||||
// icons
|
||||
import { Info, LinkIcon, Pencil, Star, Trash2 } from "lucide-react";
|
||||
// helpers
|
||||
import { findHowManyDaysLeft, renderFormattedDate } from "helpers/date-time.helper";
|
||||
import { findHowManyDaysLeft, getDate, renderFormattedDate } from "helpers/date-time.helper";
|
||||
import { copyTextToClipboard } from "helpers/string.helper";
|
||||
// constants
|
||||
import { CYCLE_STATUS } from "constants/cycle";
|
||||
@@ -50,8 +50,8 @@ export const CyclesBoardCard: FC<ICyclesBoardCard> = observer((props) => {
|
||||
|
||||
const cycleStatus = cycleDetails.status.toLocaleLowerCase();
|
||||
const isCompleted = cycleStatus === "completed";
|
||||
const endDate = new Date(cycleDetails.end_date ?? "");
|
||||
const startDate = new Date(cycleDetails.start_date ?? "");
|
||||
const endDate = getDate(cycleDetails.end_date);
|
||||
const startDate = getDate(cycleDetails.start_date);
|
||||
const isDateValid = cycleDetails.start_date || cycleDetails.end_date;
|
||||
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
|
||||
|
||||
@@ -12,7 +12,7 @@ import { CustomMenu, Tooltip, CircularProgressIndicator, CycleGroupIcon, AvatarG
|
||||
// icons
|
||||
import { Check, Info, LinkIcon, Pencil, Star, Trash2, User2 } from "lucide-react";
|
||||
// helpers
|
||||
import { findHowManyDaysLeft, renderFormattedDate } from "helpers/date-time.helper";
|
||||
import { findHowManyDaysLeft, getDate, renderFormattedDate } from "helpers/date-time.helper";
|
||||
import { copyTextToClipboard } from "helpers/string.helper";
|
||||
// constants
|
||||
import { CYCLE_STATUS } from "constants/cycle";
|
||||
@@ -137,8 +137,8 @@ export const CyclesListItem: FC<TCyclesListItem> = observer((props) => {
|
||||
// TODO: change this logic once backend fix the response
|
||||
const cycleStatus = cycleDetails.status ? (cycleDetails.status.toLocaleLowerCase() as TCycleGroups) : "draft";
|
||||
const isCompleted = cycleStatus === "completed";
|
||||
const endDate = new Date(cycleDetails.end_date ?? "");
|
||||
const startDate = new Date(cycleDetails.start_date ?? "");
|
||||
const endDate = getDate(cycleDetails.end_date);
|
||||
const startDate = getDate(cycleDetails.start_date);
|
||||
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { DateRangeDropdown, ProjectDropdown } from "components/dropdowns";
|
||||
// ui
|
||||
import { Button, Input, TextArea } from "@plane/ui";
|
||||
// helpers
|
||||
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { getDate, renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
// types
|
||||
import { ICycle } from "@plane/types";
|
||||
|
||||
@@ -135,8 +135,8 @@ export const CycleForm: React.FC<Props> = (props) => {
|
||||
className="h-7"
|
||||
minDate={new Date()}
|
||||
value={{
|
||||
from: startDateValue ? new Date(startDateValue) : undefined,
|
||||
to: endDateValue ? new Date(endDateValue) : undefined,
|
||||
from: getDate(startDateValue),
|
||||
to: getDate(endDateValue),
|
||||
}}
|
||||
onSelect={(val) => {
|
||||
onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { GanttChartRoot, IBlockUpdateData, CycleGanttSidebar } from "components/
|
||||
import { CycleGanttBlock } from "components/cycles";
|
||||
// types
|
||||
import { ICycle } from "@plane/types";
|
||||
import { getDate } from "helpers/date-time.helper";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
|
||||
@@ -45,8 +46,8 @@ export const CyclesListGanttChartView: FC<Props> = observer((props) => {
|
||||
data: block,
|
||||
id: block?.id ?? "",
|
||||
sort_order: block?.sort_order ?? 0,
|
||||
start_date: new Date(block?.start_date ?? ""),
|
||||
target_date: new Date(block?.end_date ?? ""),
|
||||
start_date: getDate(block?.start_date),
|
||||
target_date: getDate(block?.end_date),
|
||||
}));
|
||||
|
||||
return structuredBlocks;
|
||||
|
||||
@@ -96,10 +96,10 @@ export const CycleCreateUpdateModal: React.FC<CycleModalProps> = (props) => {
|
||||
});
|
||||
};
|
||||
|
||||
const dateChecker = async (payload: CycleDateCheckData) => {
|
||||
const dateChecker = async (projectId: string, payload: CycleDateCheckData) => {
|
||||
let status = false;
|
||||
|
||||
await cycleService.cycleDateCheck(workspaceSlug as string, projectId as string, payload).then((res) => {
|
||||
await cycleService.cycleDateCheck(workspaceSlug, projectId, payload).then((res) => {
|
||||
status = res.status;
|
||||
});
|
||||
|
||||
@@ -117,13 +117,13 @@ export const CycleCreateUpdateModal: React.FC<CycleModalProps> = (props) => {
|
||||
|
||||
if (payload.start_date && payload.end_date) {
|
||||
if (data?.start_date && data?.end_date)
|
||||
isDateValid = await dateChecker({
|
||||
isDateValid = await dateChecker(payload.project_id ?? projectId, {
|
||||
start_date: payload.start_date,
|
||||
end_date: payload.end_date,
|
||||
cycle_id: data.id,
|
||||
});
|
||||
else
|
||||
isDateValid = await dateChecker({
|
||||
isDateValid = await dateChecker(payload.project_id ?? projectId, {
|
||||
start_date: payload.start_date,
|
||||
end_date: payload.end_date,
|
||||
});
|
||||
|
||||
@@ -18,8 +18,8 @@ import { Avatar, CustomMenu, Loader, LayersIcon } from "@plane/ui";
|
||||
// icons
|
||||
import { ChevronDown, LinkIcon, Trash2, UserCircle2, AlertCircle, ChevronRight, CalendarClock } from "lucide-react";
|
||||
// helpers
|
||||
import { findHowManyDaysLeft, getDate, renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { copyUrlToClipboard } from "helpers/string.helper";
|
||||
import { findHowManyDaysLeft, renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
// types
|
||||
import { ICycle } from "@plane/types";
|
||||
// constants
|
||||
@@ -186,8 +186,11 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
const cycleStatus = cycleDetails?.status.toLocaleLowerCase();
|
||||
const isCompleted = cycleStatus === "completed";
|
||||
|
||||
const isStartValid = new Date(`${cycleDetails?.start_date}`) <= new Date();
|
||||
const isEndValid = new Date(`${cycleDetails?.end_date}`) >= new Date(`${cycleDetails?.start_date}`);
|
||||
const startDate = getDate(cycleDetails?.start_date);
|
||||
const endDate = getDate(cycleDetails?.end_date);
|
||||
|
||||
const isStartValid = startDate && startDate <= new Date();
|
||||
const isEndValid = endDate && startDate && endDate >= startDate;
|
||||
|
||||
const progressPercentage = cycleDetails
|
||||
? isCompleted && cycleDetails?.progress_snapshot
|
||||
@@ -317,8 +320,8 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
buttonVariant="background-with-text"
|
||||
minDate={new Date()}
|
||||
value={{
|
||||
from: startDateValue ? new Date(startDateValue) : undefined,
|
||||
to: endDateValue ? new Date(endDateValue) : undefined,
|
||||
from: getDate(startDateValue),
|
||||
to: getDate(endDateValue),
|
||||
}}
|
||||
onSelect={(val) => {
|
||||
onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useIssueDetail, useMember, useProject } from "hooks/store";
|
||||
// ui
|
||||
import { Avatar, AvatarGroup, ControlLink, PriorityIcon } from "@plane/ui";
|
||||
// helpers
|
||||
import { findTotalDaysInRange, renderFormattedDate } from "helpers/date-time.helper";
|
||||
import { findTotalDaysInRange, getDate, renderFormattedDate } from "helpers/date-time.helper";
|
||||
// types
|
||||
import { TIssue, TWidgetIssue } from "@plane/types";
|
||||
|
||||
@@ -34,6 +34,8 @@ export const AssignedUpcomingIssueListItem: React.FC<IssueListItemProps> = obser
|
||||
const blockedByIssueProjectDetails =
|
||||
blockedByIssues.length === 1 ? getProjectById(blockedByIssues[0]?.project_id ?? "") : null;
|
||||
|
||||
const targetDate = getDate(issueDetails.target_date);
|
||||
|
||||
return (
|
||||
<ControlLink
|
||||
href={`/${workspaceSlug}/projects/${issueDetails.project_id}/issues/${issueDetails.id}`}
|
||||
@@ -48,11 +50,7 @@ export const AssignedUpcomingIssueListItem: React.FC<IssueListItemProps> = obser
|
||||
<h6 className="text-sm flex-grow truncate">{issueDetails.name}</h6>
|
||||
</div>
|
||||
<div className="text-xs text-center">
|
||||
{issueDetails.target_date
|
||||
? isToday(new Date(issueDetails.target_date))
|
||||
? "Today"
|
||||
: renderFormattedDate(issueDetails.target_date)
|
||||
: "-"}
|
||||
{targetDate ? (isToday(targetDate) ? "Today" : renderFormattedDate(targetDate)) : "-"}
|
||||
</div>
|
||||
<div className="text-xs text-center">
|
||||
{blockedByIssues.length > 0
|
||||
@@ -83,7 +81,7 @@ export const AssignedOverdueIssueListItem: React.FC<IssueListItemProps> = observ
|
||||
const blockedByIssueProjectDetails =
|
||||
blockedByIssues.length === 1 ? getProjectById(blockedByIssues[0]?.project_id ?? "") : null;
|
||||
|
||||
const dueBy = findTotalDaysInRange(new Date(issueDetails.target_date ?? ""), new Date(), false) ?? 0;
|
||||
const dueBy = findTotalDaysInRange(getDate(issueDetails.target_date), new Date(), false) ?? 0;
|
||||
|
||||
return (
|
||||
<ControlLink
|
||||
@@ -212,7 +210,7 @@ export const CreatedOverdueIssueListItem: React.FC<IssueListItemProps> = observe
|
||||
|
||||
const projectDetails = getProjectById(issue.project_id);
|
||||
|
||||
const dueBy = findTotalDaysInRange(new Date(issue.target_date ?? ""), new Date(), false) ?? 0;
|
||||
const dueBy = findTotalDaysInRange(getDate(issue.target_date), new Date(), false) ?? 0;
|
||||
|
||||
return (
|
||||
<ControlLink
|
||||
|
||||
@@ -9,7 +9,7 @@ import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||
// components
|
||||
import { DropdownButton } from "./buttons";
|
||||
// helpers
|
||||
import { renderFormattedDate } from "helpers/date-time.helper";
|
||||
import { getDate, renderFormattedDate } from "helpers/date-time.helper";
|
||||
import { cn } from "helpers/common.helper";
|
||||
// types
|
||||
import { TDropdownProps } from "./types";
|
||||
@@ -168,8 +168,8 @@ export const DateDropdown: React.FC<Props> = (props) => {
|
||||
{...attributes.popper}
|
||||
>
|
||||
<DayPicker
|
||||
selected={value ? new Date(value) : undefined}
|
||||
defaultMonth={value ? new Date(value) : undefined}
|
||||
selected={getDate(value)}
|
||||
defaultMonth={getDate(value)}
|
||||
onSelect={(date) => {
|
||||
dropdownOnChange(date ?? null);
|
||||
}}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState, FC } from "react";
|
||||
// ui
|
||||
import { Button } from "@plane/ui";
|
||||
// helpers
|
||||
import { renderFormattedDate } from "helpers/date-time.helper";
|
||||
import { getDate, renderFormattedDate } from "helpers/date-time.helper";
|
||||
// types
|
||||
import { IExportData } from "@plane/types";
|
||||
|
||||
@@ -18,7 +18,8 @@ export const SingleExport: FC<Props> = ({ service, refreshing }) => {
|
||||
|
||||
const checkExpiry = (inputDateString: string) => {
|
||||
const currentDate = new Date();
|
||||
const expiryDate = new Date(inputDateString);
|
||||
const expiryDate = getDate(inputDateString);
|
||||
if (!expiryDate) return false;
|
||||
expiryDate.setDate(expiryDate.getDate() + 7);
|
||||
return expiryDate > currentDate;
|
||||
};
|
||||
|
||||
@@ -64,9 +64,9 @@ export const ChartViewRoot: FC<ChartViewRootProps> = observer((props) => {
|
||||
useGanttChart();
|
||||
|
||||
// rendering the block structure
|
||||
const renderBlockStructure = (view: any, blocks: IGanttBlock[] | null) =>
|
||||
const renderBlockStructure = (view: ChartDataType, blocks: IGanttBlock[] | null) =>
|
||||
blocks
|
||||
? blocks.map((block: any) => ({
|
||||
? blocks.map((block: IGanttBlock) => ({
|
||||
...block,
|
||||
position: getMonthChartItemPositionWidthInMonth(view, block),
|
||||
}))
|
||||
|
||||
@@ -6,8 +6,8 @@ export interface IGanttBlock {
|
||||
width: number;
|
||||
};
|
||||
sort_order: number;
|
||||
start_date: Date | null;
|
||||
target_date: Date | null;
|
||||
start_date: Date | undefined;
|
||||
target_date: Date | undefined;
|
||||
}
|
||||
|
||||
export interface IBlockUpdateData {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// types
|
||||
import { findTotalDaysInRange } from "helpers/date-time.helper";
|
||||
import { ChartDataType, IGanttBlock } from "../types";
|
||||
// data
|
||||
import { weeks, months } from "../data";
|
||||
@@ -167,15 +168,16 @@ export const getMonthChartItemPositionWidthInMonth = (chartData: ChartDataType,
|
||||
const { startDate } = chartData.data;
|
||||
const { start_date: itemStartDate, target_date: itemTargetDate } = itemData;
|
||||
|
||||
if (!itemStartDate || !itemTargetDate) return null;
|
||||
if (!itemStartDate || !itemTargetDate) return;
|
||||
|
||||
startDate.setHours(0, 0, 0, 0);
|
||||
itemStartDate.setHours(0, 0, 0, 0);
|
||||
itemTargetDate.setHours(0, 0, 0, 0);
|
||||
|
||||
// position code starts
|
||||
const positionTimeDifference: number = startDate.getTime() - itemStartDate.getTime();
|
||||
const positionDaysDifference: number = Math.abs(Math.floor(positionTimeDifference / (1000 * 60 * 60 * 24)));
|
||||
const positionDaysDifference = findTotalDaysInRange(startDate, itemStartDate, false);
|
||||
|
||||
if (!positionDaysDifference) return;
|
||||
|
||||
scrollPosition = positionDaysDifference * chartData.data.width;
|
||||
|
||||
var diffMonths = (itemStartDate.getFullYear() - startDate.getFullYear()) * 12;
|
||||
|
||||
@@ -21,6 +21,8 @@ import { CheckCircle2, ChevronDown, ChevronUp, Clock, FileStack, Trash2, XCircle
|
||||
import type { TInboxStatus, TInboxDetailedStatus } from "@plane/types";
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
import { ISSUE_DELETED } from "constants/event-tracker";
|
||||
//helpers
|
||||
import { getDate } from "helpers/date-time.helper";
|
||||
|
||||
type TInboxIssueActionsHeader = {
|
||||
workspaceSlug: string;
|
||||
@@ -171,11 +173,11 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
||||
const isAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
|
||||
const today = new Date();
|
||||
const tomorrow = new Date(today);
|
||||
tomorrow.setDate(today.getDate() + 1);
|
||||
const tomorrow = getDate(today);
|
||||
tomorrow?.setDate(today.getDate() + 1);
|
||||
useEffect(() => {
|
||||
if (!issueStatus || !issueStatus.snoozed_till) return;
|
||||
setDate(new Date(issueStatus.snoozed_till));
|
||||
setDate(issueStatus.snoozed_till);
|
||||
}, [issueStatus]);
|
||||
|
||||
if (!issueStatus || !issue || !inboxIssues) return <></>;
|
||||
@@ -269,19 +271,23 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
||||
{({ close }) => (
|
||||
<div className="flex h-full w-full flex-col gap-y-1">
|
||||
<DayPicker
|
||||
selected={date ? new Date(date) : undefined}
|
||||
defaultMonth={date ? new Date(date) : undefined}
|
||||
selected={getDate(date)}
|
||||
defaultMonth={getDate(date)}
|
||||
onSelect={(date) => {
|
||||
if (!date) return;
|
||||
setDate(date);
|
||||
}}
|
||||
mode="single"
|
||||
className="border border-custom-border-200 rounded-md p-3"
|
||||
disabled={[
|
||||
{
|
||||
before: tomorrow,
|
||||
},
|
||||
]}
|
||||
disabled={
|
||||
tomorrow
|
||||
? [
|
||||
{
|
||||
before: tomorrow,
|
||||
},
|
||||
]
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
variant="primary"
|
||||
@@ -289,7 +295,7 @@ export const InboxIssueActionsHeader: FC<TInboxIssueActionsHeader> = observer((p
|
||||
close();
|
||||
inboxIssueOperations.updateInboxIssueStatus({
|
||||
status: 0,
|
||||
snoozed_till: new Date(date),
|
||||
snoozed_till: date,
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -28,7 +28,7 @@ export const InboxIssueStatus: React.FC<Props> = observer((props) => {
|
||||
if (!inboxIssueStatusDetail) return <></>;
|
||||
|
||||
const isSnoozedDatePassed =
|
||||
inboxIssueDetail.status === 0 && new Date(inboxIssueDetail.snoozed_till ?? "") < new Date();
|
||||
inboxIssueDetail.status === 0 && !!inboxIssueDetail.snoozed_till && inboxIssueDetail.snoozed_till < new Date();
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -46,7 +46,7 @@ export const InboxIssueStatus: React.FC<Props> = observer((props) => {
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
inboxIssueDetail.duplicate_to ?? "",
|
||||
new Date(inboxIssueDetail.snoozed_till ?? "")
|
||||
inboxIssueDetail.snoozed_till
|
||||
)
|
||||
) : (
|
||||
<span>{inboxIssueStatusDetail.title}</span>
|
||||
|
||||
@@ -9,7 +9,7 @@ import { DateDropdown, PriorityDropdown, MemberDropdown, StateDropdown } from "c
|
||||
// icons
|
||||
import { DoubleCircleIcon, StateGroupIcon, UserGroupIcon } from "@plane/ui";
|
||||
// helper
|
||||
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { getDate, renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
|
||||
type Props = {
|
||||
workspaceSlug: string;
|
||||
@@ -33,7 +33,7 @@ export const InboxIssueDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
|
||||
const projectDetails = issue ? getProjectById(issue.project_id) : null;
|
||||
|
||||
const minDate = issue.start_date ? new Date(issue.start_date) : null;
|
||||
const minDate = issue.start_date ? getDate(issue.start_date) : null;
|
||||
minDate?.setDate(minDate.getDate());
|
||||
|
||||
const currentIssueState = projectStates?.find((s) => s.id === issue.state_id);
|
||||
|
||||
@@ -28,12 +28,13 @@ import {
|
||||
IssueLabel,
|
||||
ArchiveIssueModal,
|
||||
} from "components/issues";
|
||||
// components
|
||||
import { IssueSubscription } from "./subscription";
|
||||
import { DateDropdown, EstimateDropdown, PriorityDropdown, MemberDropdown, StateDropdown } from "components/dropdowns";
|
||||
// icons
|
||||
import { ArchiveIcon, ContrastIcon, DiceIcon, DoubleCircleIcon, RelatedIcon, Tooltip, UserGroupIcon } from "@plane/ui";
|
||||
// helpers
|
||||
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { getDate, renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { copyTextToClipboard } from "helpers/string.helper";
|
||||
import { cn } from "helpers/common.helper";
|
||||
import { shouldHighlightIssueDueDate } from "helpers/issue.helper";
|
||||
@@ -99,10 +100,10 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
const isInArchivableGroup =
|
||||
!!stateDetails && [STATE_GROUPS.completed.key, STATE_GROUPS.cancelled.key].includes(stateDetails?.group);
|
||||
|
||||
const minDate = issue.start_date ? new Date(issue.start_date) : null;
|
||||
const minDate = issue.start_date ? getDate(issue.start_date) : null;
|
||||
minDate?.setDate(minDate.getDate());
|
||||
|
||||
const maxDate = issue.target_date ? new Date(issue.target_date) : null;
|
||||
const maxDate = issue.target_date ? getDate(issue.target_date) : null;
|
||||
maxDate?.setDate(maxDate.getDate());
|
||||
|
||||
return (
|
||||
|
||||
@@ -12,6 +12,8 @@ import { ICycleIssuesFilter } from "store/issue/cycle";
|
||||
import { IModuleIssuesFilter } from "store/issue/module";
|
||||
import { IProjectIssuesFilter } from "store/issue/project";
|
||||
import { IProjectViewIssuesFilter } from "store/issue/project-views";
|
||||
// helpers
|
||||
import { getDate } from "helpers/date-time.helper";
|
||||
|
||||
interface Props {
|
||||
issuesFilterStore: IProjectIssuesFilter | IModuleIssuesFilter | ICycleIssuesFilter | IProjectViewIssuesFilter;
|
||||
@@ -47,8 +49,10 @@ export const CalendarMonthsDropdown: React.FC<Props> = observer((props: Props) =
|
||||
|
||||
const daysList = Object.keys(allDaysOfActiveWeek);
|
||||
|
||||
const firstDay = new Date(daysList[0]);
|
||||
const lastDay = new Date(daysList[daysList.length - 1]);
|
||||
const firstDay = getDate(daysList[0]);
|
||||
const lastDay = getDate(daysList[daysList.length - 1]);
|
||||
|
||||
if (!firstDay || !lastDay) return "Week view";
|
||||
|
||||
if (firstDay.getMonth() === lastDay.getMonth() && firstDay.getFullYear() === lastDay.getFullYear())
|
||||
return `${MONTHS_LIST[firstDay.getMonth() + 1].title} ${firstDay.getFullYear()}`;
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
StateDropdown,
|
||||
} from "components/dropdowns";
|
||||
// helpers
|
||||
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { getDate, renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { shouldHighlightIssueDueDate } from "helpers/issue.helper";
|
||||
import { cn } from "helpers/common.helper";
|
||||
// types
|
||||
@@ -229,10 +229,10 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
||||
|
||||
const defaultLabelOptions = issue?.label_ids?.map((id) => labelMap[id]) || [];
|
||||
|
||||
const minDate = issue.start_date ? new Date(issue.start_date) : null;
|
||||
const minDate = getDate(issue.start_date);
|
||||
minDate?.setDate(minDate.getDate());
|
||||
|
||||
const maxDate = issue.target_date ? new Date(issue.target_date) : null;
|
||||
const maxDate = getDate(issue.target_date);
|
||||
maxDate?.setDate(maxDate.getDate());
|
||||
|
||||
return (
|
||||
@@ -284,7 +284,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
||||
<DateDropdown
|
||||
value={issue.start_date ?? null}
|
||||
onChange={handleStartDate}
|
||||
maxDate={maxDate ?? undefined}
|
||||
maxDate={maxDate}
|
||||
placeholder="Start date"
|
||||
icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
|
||||
buttonVariant={issue.start_date ? "border-with-text" : "border-without-text"}
|
||||
@@ -300,7 +300,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
||||
<DateDropdown
|
||||
value={issue?.target_date ?? null}
|
||||
onChange={handleTargetDate}
|
||||
minDate={minDate ?? undefined}
|
||||
minDate={minDate}
|
||||
placeholder="Due date"
|
||||
icon={<CalendarCheck2 className="h-3 w-3 flex-shrink-0" />}
|
||||
buttonVariant={issue.target_date ? "border-with-text" : "border-without-text"}
|
||||
|
||||
@@ -6,9 +6,9 @@ import { useProjectState } from "hooks/store";
|
||||
// components
|
||||
import { DateDropdown } from "components/dropdowns";
|
||||
// helpers
|
||||
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { shouldHighlightIssueDueDate } from "helpers/issue.helper";
|
||||
import { cn } from "helpers/common.helper";
|
||||
import { getDate, renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { shouldHighlightIssueDueDate } from "helpers/issue.helper";
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
|
||||
@@ -30,7 +30,7 @@ export const SpreadsheetDueDateColumn: React.FC<Props> = observer((props: Props)
|
||||
<div className="h-11 border-b-[0.5px] border-custom-border-200">
|
||||
<DateDropdown
|
||||
value={issue.target_date}
|
||||
minDate={issue.start_date ? new Date(issue.start_date) : undefined}
|
||||
minDate={getDate(issue.start_date)}
|
||||
onChange={(data) => {
|
||||
const targetDate = data ? renderFormattedPayloadDate(data) : null;
|
||||
onChange(
|
||||
|
||||
@@ -4,7 +4,7 @@ import { CalendarClock } from "lucide-react";
|
||||
// components
|
||||
import { DateDropdown } from "components/dropdowns";
|
||||
// helpers
|
||||
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { getDate, renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
|
||||
@@ -22,7 +22,7 @@ export const SpreadsheetStartDateColumn: React.FC<Props> = observer((props: Prop
|
||||
<div className="h-11 border-b-[0.5px] border-custom-border-200">
|
||||
<DateDropdown
|
||||
value={issue.start_date}
|
||||
maxDate={issue.target_date ? new Date(issue.target_date) : undefined}
|
||||
maxDate={getDate(issue.target_date)}
|
||||
onChange={(data) => {
|
||||
const startDate = data ? renderFormattedPayloadDate(data) : null;
|
||||
onChange(
|
||||
|
||||
@@ -26,10 +26,10 @@ import {
|
||||
MemberDropdown,
|
||||
StateDropdown,
|
||||
} from "components/dropdowns";
|
||||
// ui
|
||||
//ui
|
||||
import { Button, CustomMenu, Input, Loader, ToggleSwitch } from "@plane/ui";
|
||||
// helpers
|
||||
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { getDate, renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
// types
|
||||
import type { TIssue, ISearchIssueResponse } from "@plane/types";
|
||||
|
||||
@@ -236,10 +236,10 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
||||
const startDate = watch("start_date");
|
||||
const targetDate = watch("target_date");
|
||||
|
||||
const minDate = startDate ? new Date(startDate) : null;
|
||||
const minDate = getDate(startDate);
|
||||
minDate?.setDate(minDate.getDate());
|
||||
|
||||
const maxDate = targetDate ? new Date(targetDate) : null;
|
||||
const maxDate = getDate(targetDate);
|
||||
maxDate?.setDate(maxDate.getDate());
|
||||
|
||||
const projectDetails = getProjectById(projectId);
|
||||
|
||||
@@ -26,9 +26,9 @@ import {
|
||||
} from "components/issues";
|
||||
import { DateDropdown, EstimateDropdown, PriorityDropdown, MemberDropdown, StateDropdown } from "components/dropdowns";
|
||||
// components
|
||||
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
// helpers
|
||||
import { cn } from "helpers/common.helper";
|
||||
import { getDate, renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { shouldHighlightIssueDueDate } from "helpers/issue.helper";
|
||||
|
||||
interface IPeekOverviewProperties {
|
||||
@@ -54,10 +54,10 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
||||
const isEstimateEnabled = projectDetails?.estimate;
|
||||
const stateDetails = getStateById(issue.state_id);
|
||||
|
||||
const minDate = issue.start_date ? new Date(issue.start_date) : null;
|
||||
const minDate = getDate(issue.start_date);
|
||||
minDate?.setDate(minDate.getDate());
|
||||
|
||||
const maxDate = issue.target_date ? new Date(issue.target_date) : null;
|
||||
const maxDate = getDate(issue.target_date);
|
||||
maxDate?.setDate(maxDate.getDate());
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,7 +6,7 @@ import { DateRangeDropdown, ProjectDropdown, MemberDropdown } from "components/d
|
||||
// ui
|
||||
import { Button, Input, TextArea } from "@plane/ui";
|
||||
// helpers
|
||||
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { getDate, renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
// types
|
||||
import { IModule } from "@plane/types";
|
||||
|
||||
@@ -147,8 +147,8 @@ export const ModuleForm: React.FC<Props> = (props) => {
|
||||
className="h-7"
|
||||
minDate={new Date()}
|
||||
value={{
|
||||
from: startDateValue ? new Date(startDateValue) : undefined,
|
||||
to: endDateValue ? new Date(endDateValue) : undefined,
|
||||
from: getDate(startDateValue),
|
||||
to: getDate(endDateValue),
|
||||
}}
|
||||
onSelect={(val) => {
|
||||
onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { GanttChartRoot, IBlockUpdateData, ModuleGanttSidebar } from "components
|
||||
import { ModuleGanttBlock } from "components/modules";
|
||||
// types
|
||||
import { IModule } from "@plane/types";
|
||||
import { getDate } from "helpers/date-time.helper";
|
||||
|
||||
export const ModulesListGanttChartView: React.FC = observer(() => {
|
||||
// router
|
||||
@@ -32,8 +33,8 @@ export const ModulesListGanttChartView: React.FC = observer(() => {
|
||||
data: block,
|
||||
id: block.id,
|
||||
sort_order: block.sort_order,
|
||||
start_date: block.start_date ? new Date(block.start_date) : null,
|
||||
target_date: block.target_date ? new Date(block.target_date) : null,
|
||||
start_date: getDate(block.start_date),
|
||||
target_date: getDate(block.target_date),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { CreateUpdateModuleModal, DeleteModuleModal } from "components/modules";
|
||||
import { Avatar, AvatarGroup, CustomMenu, LayersIcon, Tooltip } from "@plane/ui";
|
||||
// helpers
|
||||
import { copyUrlToClipboard } from "helpers/string.helper";
|
||||
import { renderFormattedDate } from "helpers/date-time.helper";
|
||||
import { getDate, renderFormattedDate } from "helpers/date-time.helper";
|
||||
// constants
|
||||
import { MODULE_STATUS } from "constants/module";
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
@@ -135,8 +135,8 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
|
||||
|
||||
const completionPercentage = (moduleDetails.completed_issues / moduleTotalIssues) * 100;
|
||||
|
||||
const endDate = new Date(moduleDetails.target_date ?? "");
|
||||
const startDate = new Date(moduleDetails.start_date ?? "");
|
||||
const endDate = getDate(moduleDetails.target_date);
|
||||
const startDate = getDate(moduleDetails.start_date);
|
||||
|
||||
const isDateValid = moduleDetails.target_date || moduleDetails.start_date;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { CreateUpdateModuleModal, DeleteModuleModal } from "components/modules";
|
||||
import { Avatar, AvatarGroup, CircularProgressIndicator, CustomMenu, Tooltip } from "@plane/ui";
|
||||
// helpers
|
||||
import { copyUrlToClipboard } from "helpers/string.helper";
|
||||
import { renderFormattedDate } from "helpers/date-time.helper";
|
||||
import { getDate, renderFormattedDate } from "helpers/date-time.helper";
|
||||
// constants
|
||||
import { MODULE_STATUS } from "constants/module";
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
@@ -129,8 +129,8 @@ export const ModuleListItem: React.FC<Props> = observer((props) => {
|
||||
const completionPercentage =
|
||||
((moduleDetails.completed_issues + moduleDetails.cancelled_issues) / moduleDetails.total_issues) * 100;
|
||||
|
||||
const endDate = new Date(moduleDetails.target_date ?? "");
|
||||
const startDate = new Date(moduleDetails.start_date ?? "");
|
||||
const endDate = getDate(moduleDetails.target_date);
|
||||
const startDate = getDate(moduleDetails.start_date);
|
||||
|
||||
const renderDate = moduleDetails.start_date || moduleDetails.target_date;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import { DateRangeDropdown, MemberDropdown } from "components/dropdowns";
|
||||
// ui
|
||||
import { CustomMenu, Loader, LayersIcon, CustomSelect, ModuleStatusIcon, UserGroupIcon } from "@plane/ui";
|
||||
// helpers
|
||||
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { getDate, renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { copyUrlToClipboard } from "helpers/string.helper";
|
||||
// types
|
||||
import { ILinkDetails, IModule, ModuleLink } from "@plane/types";
|
||||
@@ -201,8 +201,10 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
});
|
||||
}, [moduleDetails, reset]);
|
||||
|
||||
const isStartValid = new Date(`${moduleDetails?.start_date}`) <= new Date();
|
||||
const isEndValid = new Date(`${moduleDetails?.target_date}`) >= new Date(`${moduleDetails?.start_date}`);
|
||||
const startDate = getDate(moduleDetails?.start_date);
|
||||
const endDate = getDate(moduleDetails?.target_date);
|
||||
const isStartValid = startDate && startDate <= new Date();
|
||||
const isEndValid = startDate && endDate && endDate >= startDate;
|
||||
|
||||
const progressPercentage = moduleDetails
|
||||
? Math.round((moduleDetails.completed_issues / moduleDetails.total_issues) * 100)
|
||||
@@ -342,26 +344,30 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
<Controller
|
||||
control={control}
|
||||
name="target_date"
|
||||
render={({ field: { value: endDateValue, onChange: onChangeEndDate } }) => (
|
||||
<DateRangeDropdown
|
||||
buttonContainerClassName="w-full"
|
||||
buttonVariant="background-with-text"
|
||||
minDate={new Date()}
|
||||
value={{
|
||||
from: startDateValue ? new Date(startDateValue) : undefined,
|
||||
to: endDateValue ? new Date(endDateValue) : undefined,
|
||||
}}
|
||||
onSelect={(val) => {
|
||||
onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null);
|
||||
onChangeEndDate(val?.to ? renderFormattedPayloadDate(val.to) : null);
|
||||
handleDateChange(val?.from, val?.to);
|
||||
}}
|
||||
placeholder={{
|
||||
from: "Start date",
|
||||
to: "Target date",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
render={({ field: { value: endDateValue, onChange: onChangeEndDate } }) => {
|
||||
const startDate = getDate(startDateValue);
|
||||
const endDate = getDate(endDateValue);
|
||||
return (
|
||||
<DateRangeDropdown
|
||||
buttonContainerClassName="w-full"
|
||||
buttonVariant="background-with-text"
|
||||
minDate={new Date()}
|
||||
value={{
|
||||
from: startDate,
|
||||
to: endDate,
|
||||
}}
|
||||
onSelect={(val) => {
|
||||
onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null);
|
||||
onChangeEndDate(val?.to ? renderFormattedPayloadDate(val.to) : null);
|
||||
handleDateChange(val?.from, val?.to);
|
||||
}}
|
||||
placeholder={{
|
||||
from: "Start date",
|
||||
to: "Target date",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -13,7 +13,7 @@ import { ArchiveIcon, CustomMenu, Tooltip } from "@plane/ui";
|
||||
import { snoozeOptions } from "constants/notification";
|
||||
// helper
|
||||
import { replaceUnderscoreIfSnakeCase, truncateText, stripAndTruncateHTML } from "helpers/string.helper";
|
||||
import { calculateTimeAgo, renderFormattedTime, renderFormattedDate } from "helpers/date-time.helper";
|
||||
import { calculateTimeAgo, renderFormattedTime, renderFormattedDate, getDate } from "helpers/date-time.helper";
|
||||
// type
|
||||
import type { IUserNotification, NotificationType } from "@plane/types";
|
||||
// constants
|
||||
@@ -119,7 +119,9 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
||||
const notificationField = notification.data.issue_activity.field;
|
||||
const notificationTriggeredBy = notification.triggered_by_details;
|
||||
|
||||
if (isSnoozedTabOpen && new Date(notification.snoozed_till!) < new Date()) return null;
|
||||
const snoozedTillDate = getDate(notification?.snoozed_till);
|
||||
|
||||
if (snoozedTillDate && isSnoozedTabOpen && snoozedTillDate < new Date()) return null;
|
||||
|
||||
return (
|
||||
<Link
|
||||
|
||||
@@ -12,6 +12,8 @@ import useToast from "hooks/use-toast";
|
||||
import { Button, CustomSelect } from "@plane/ui";
|
||||
// types
|
||||
import type { IUserNotification } from "@plane/types";
|
||||
// helpers
|
||||
import { getDate } from "helpers/date-time.helper";
|
||||
|
||||
type SnoozeModalProps = {
|
||||
isOpen: boolean;
|
||||
@@ -60,7 +62,7 @@ export const SnoozeNotificationModal: FC<SnoozeModalProps> = (props) => {
|
||||
|
||||
if (!formDataDate) return timeStamps;
|
||||
|
||||
const isToday = today.toDateString() === new Date(formDataDate).toDateString();
|
||||
const isToday = today.toDateString() === getDate(formDataDate)?.toDateString();
|
||||
|
||||
if (!isToday) return timeStamps;
|
||||
|
||||
@@ -93,9 +95,9 @@ export const SnoozeNotificationModal: FC<SnoozeModalProps> = (props) => {
|
||||
);
|
||||
const minutes = parseInt(time[1]);
|
||||
|
||||
const dateTime = new Date(formData.date);
|
||||
dateTime.setHours(hours);
|
||||
dateTime.setMinutes(minutes);
|
||||
const dateTime: Date | undefined = getDate(formData?.date);
|
||||
dateTime?.setHours(hours);
|
||||
dateTime?.setMinutes(minutes);
|
||||
|
||||
await handleSubmitSnooze(notification.id, dateTime).then(() => {
|
||||
handleClose();
|
||||
@@ -214,10 +216,11 @@ export const SnoozeNotificationModal: FC<SnoozeModalProps> = (props) => {
|
||||
onClick={() => {
|
||||
setValue("period", "AM");
|
||||
}}
|
||||
className={`flex h-full w-1/2 cursor-pointer items-center justify-center text-center ${watch("period") === "AM"
|
||||
className={`flex h-full w-1/2 cursor-pointer items-center justify-center text-center ${
|
||||
watch("period") === "AM"
|
||||
? "bg-custom-primary-100/90 text-custom-primary-0"
|
||||
: "bg-custom-background-80"
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
AM
|
||||
</div>
|
||||
@@ -225,10 +228,11 @@ export const SnoozeNotificationModal: FC<SnoozeModalProps> = (props) => {
|
||||
onClick={() => {
|
||||
setValue("period", "PM");
|
||||
}}
|
||||
className={`flex h-full w-1/2 cursor-pointer items-center justify-center text-center ${watch("period") === "PM"
|
||||
className={`flex h-full w-1/2 cursor-pointer items-center justify-center text-center ${
|
||||
watch("period") === "PM"
|
||||
? "bg-custom-primary-100/90 text-custom-primary-0"
|
||||
: "bg-custom-background-80"
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
PM
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,12 @@ export const INBOX_STATUS: {
|
||||
status: number;
|
||||
icon: LucideIcon;
|
||||
title: string;
|
||||
description: (workspaceSlug: string, projectId: string, issueId: string, snoozedTillDate: Date) => JSX.Element;
|
||||
description: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
snoozedTillDate: Date | undefined
|
||||
) => JSX.Element;
|
||||
textColor: (snoozeDatePassed: boolean) => string;
|
||||
bgColor: (snoozeDatePassed: boolean) => string;
|
||||
borderColor: (snoozeDatePassed: boolean) => string;
|
||||
|
||||
@@ -91,7 +91,7 @@ const UserNotificationContextProvider: React.FC<{
|
||||
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
|
||||
const { selectedTab, snoozed, archived, readNotification, selectedNotificationForSnooze } = state;
|
||||
const { selectedTab, snoozed, archived, readNotification } = state;
|
||||
|
||||
const params = {
|
||||
type: snoozed || archived || readNotification ? undefined : selectedTab,
|
||||
@@ -207,7 +207,7 @@ const UserNotificationContextProvider: React.FC<{
|
||||
(previousNotifications: any) =>
|
||||
previousNotifications?.map((notification: any) =>
|
||||
notification.id === notificationId
|
||||
? { ...notification, snoozed_till: isSnoozed ? null : new Date(dateTime!) }
|
||||
? { ...notification, snoozed_till: isSnoozed ? null : dateTime }
|
||||
: notification
|
||||
) || [],
|
||||
false
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { differenceInDays, format, formatDistanceToNow, isAfter, isEqual, isValid, parseISO } from "date-fns";
|
||||
import { isNil } from "lodash";
|
||||
import isNumber from "lodash/isNumber";
|
||||
|
||||
// Format Date Helpers
|
||||
/**
|
||||
@@ -8,10 +8,11 @@ import { isNil } from "lodash";
|
||||
* @param {Date | string} date
|
||||
* @example renderFormattedDate("2024-01-01") // Jan 01, 2024
|
||||
*/
|
||||
export const renderFormattedDate = (date: string | Date): string | null => {
|
||||
if (!date) return null;
|
||||
export const renderFormattedDate = (date: string | Date | undefined | null): string | null => {
|
||||
// Parse the date to check if it is valid
|
||||
const parsedDate = new Date(date);
|
||||
const parsedDate = getDate(date);
|
||||
// return if undefined
|
||||
if (!parsedDate) return null;
|
||||
// Check if the parsed date is valid before formatting
|
||||
if (!isValid(parsedDate)) return null; // Return null for invalid dates
|
||||
// Format the date in format (MMM dd, yyyy)
|
||||
@@ -26,9 +27,10 @@ export const renderFormattedDate = (date: string | Date): string | null => {
|
||||
* @example renderShortDateFormat("2024-01-01") // Jan 01
|
||||
*/
|
||||
export const renderFormattedDateWithoutYear = (date: string | Date): string => {
|
||||
if (!date) return "";
|
||||
// Parse the date to check if it is valid
|
||||
const parsedDate = new Date(date);
|
||||
const parsedDate = getDate(date);
|
||||
// return if undefined
|
||||
if (!parsedDate) return "";
|
||||
// Check if the parsed date is valid before formatting
|
||||
if (!isValid(parsedDate)) return ""; // Return empty string for invalid dates
|
||||
// Format the date in short format (MMM dd)
|
||||
@@ -43,9 +45,10 @@ export const renderFormattedDateWithoutYear = (date: string | Date): string => {
|
||||
* @example renderFormattedPayloadDate("Jan 01, 20224") // "2024-01-01"
|
||||
*/
|
||||
export const renderFormattedPayloadDate = (date: Date | string): string | null => {
|
||||
if (!date) return null;
|
||||
// Parse the date to check if it is valid
|
||||
const parsedDate = new Date(date);
|
||||
const parsedDate = getDate(date);
|
||||
// return if undefined
|
||||
if (!parsedDate) return null;
|
||||
// Check if the parsed date is valid before formatting
|
||||
if (!isValid(parsedDate)) return null; // Return null for invalid dates
|
||||
// Format the date in payload format (yyyy-mm-dd)
|
||||
@@ -62,10 +65,14 @@ export const renderFormattedPayloadDate = (date: Date | string): string | null =
|
||||
* @example renderFormattedTime("2024-01-01 13:00:00") // 13:00
|
||||
* @example renderFormattedTime("2024-01-01 13:00:00", "12-hour") // 01:00 PM
|
||||
*/
|
||||
export const renderFormattedTime = (date: string | Date, timeFormat: "12-hour" | "24-hour" = "24-hour"): string => {
|
||||
if (!date || date === "") return "";
|
||||
export const renderFormattedTime = (
|
||||
date: string | Date | undefined | null,
|
||||
timeFormat: "12-hour" | "24-hour" = "24-hour"
|
||||
): string => {
|
||||
// Parse the date to check if it is valid
|
||||
const parsedDate = new Date(date);
|
||||
const parsedDate = getDate(date);
|
||||
// return if undefined
|
||||
if (!parsedDate) return "";
|
||||
// Check if the parsed date is valid
|
||||
if (!isValid(parsedDate)) return ""; // Return empty string for invalid dates
|
||||
// Format the date in 12 hour format if in12HourFormat is true
|
||||
@@ -92,10 +99,11 @@ export const findTotalDaysInRange = (
|
||||
endDate: Date | string | undefined | null,
|
||||
inclusive: boolean = true
|
||||
): number | undefined => {
|
||||
if (!startDate || !endDate) return undefined;
|
||||
// Parse the dates to check if they are valid
|
||||
const parsedStartDate = new Date(startDate);
|
||||
const parsedEndDate = new Date(endDate);
|
||||
const parsedStartDate = getDate(startDate);
|
||||
const parsedEndDate = getDate(endDate);
|
||||
// return if undefined
|
||||
if (!parsedStartDate || !parsedEndDate) return;
|
||||
// Check if the parsed dates are valid before calculating the difference
|
||||
if (!isValid(parsedStartDate) || !isValid(parsedEndDate)) return 0; // Return 0 for invalid dates
|
||||
// Calculate the difference in days
|
||||
@@ -131,6 +139,7 @@ export const calculateTimeAgo = (time: string | number | Date | null): string =>
|
||||
if (!time) return "";
|
||||
// Parse the time to check if it is valid
|
||||
const parsedTime = typeof time === "string" || typeof time === "number" ? parseISO(String(time)) : time;
|
||||
// return if undefined
|
||||
if (!parsedTime) return ""; // Return empty string for invalid dates
|
||||
// Format the time in the form of amount of time passed since the event happened
|
||||
const distance = formatDistanceToNow(parsedTime, { addSuffix: true });
|
||||
@@ -164,7 +173,7 @@ export const isDateGreaterThanToday = (dateStr: string): boolean => {
|
||||
* @example getWeekNumber(new Date("2023-09-01")) // 35
|
||||
*/
|
||||
export const getWeekNumberOfDate = (date: Date): number => {
|
||||
const currentDate = new Date(date);
|
||||
const currentDate = date;
|
||||
// Adjust the starting day to Sunday (0) instead of Monday (1)
|
||||
const startDate = new Date(currentDate.getFullYear(), 0, 1);
|
||||
// Calculate the number of days between currentDate and startDate
|
||||
@@ -186,10 +195,30 @@ export const checkIfDatesAreEqual = (
|
||||
date1: Date | string | null | undefined,
|
||||
date2: Date | string | null | undefined
|
||||
): boolean => {
|
||||
if (isNil(date1) && isNil(date2)) return true;
|
||||
if (isNil(date1) || isNil(date2)) return false;
|
||||
const parsedDate1 = getDate(date1);
|
||||
const parsedDate2 = getDate(date2);
|
||||
// return if undefined
|
||||
if (!parsedDate1 && !parsedDate2) return true;
|
||||
if (!parsedDate1 || !parsedDate2) return false;
|
||||
|
||||
const parsedDate1 = new Date(date1);
|
||||
const parsedDate2 = new Date(date2);
|
||||
return isEqual(parsedDate1, parsedDate2);
|
||||
};
|
||||
|
||||
/**
|
||||
* This method returns a date from string of type yyyy-mm-dd
|
||||
* This method is recommended to use instead of new Date() as this does not introduce any timezone offsets
|
||||
* @param date
|
||||
* @returns date or undefined
|
||||
*/
|
||||
export const getDate = (date: string | Date | undefined | null): Date | undefined => {
|
||||
if (!date || date === "") return;
|
||||
|
||||
if (typeof date !== "string" && !(date instanceof String)) return date;
|
||||
const [yearString, monthString, dayString] = date.substring(0, 10).split("-");
|
||||
const year = parseInt(yearString);
|
||||
const month = parseInt(monthString);
|
||||
const day = parseInt(dayString);
|
||||
if (!isNumber(year) || !isNumber(month) || !isNumber(day)) return;
|
||||
|
||||
return new Date(year, month - 1, day);
|
||||
};
|
||||
@@ -1,5 +1,7 @@
|
||||
// types
|
||||
import { IIssueFilterOptions } from "@plane/types";
|
||||
import differenceInCalendarDays from "date-fns/differenceInCalendarDays";
|
||||
import { getDate } from "./date-time.helper";
|
||||
|
||||
export const calculateTotalFilters = (filters: IIssueFilterOptions): number =>
|
||||
filters && Object.keys(filters).length > 0
|
||||
@@ -14,3 +16,35 @@ export const calculateTotalFilters = (filters: IIssueFilterOptions): number =>
|
||||
.reduce((curr, prev) => curr + prev, 0)
|
||||
: 0;
|
||||
|
||||
/**
|
||||
* @description checks if the date satisfies the filter
|
||||
* @param {Date} date
|
||||
* @param {string} filter
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const satisfiesDateFilter = (date: Date, filter: string): boolean => {
|
||||
const [value, operator, from] = filter.split(";");
|
||||
|
||||
const dateValue = getDate(value);
|
||||
if (!from && dateValue) {
|
||||
if (operator === "after") return date >= dateValue;
|
||||
if (operator === "before") return date <= dateValue;
|
||||
}
|
||||
|
||||
if (from === "fromnow") {
|
||||
if (operator === "before") {
|
||||
if (value === "1_weeks") return differenceInCalendarDays(date, new Date()) <= -7;
|
||||
if (value === "2_weeks") return differenceInCalendarDays(date, new Date()) <= -14;
|
||||
if (value === "1_months") return differenceInCalendarDays(date, new Date()) <= -30;
|
||||
}
|
||||
|
||||
if (operator === "after") {
|
||||
if (value === "1_weeks") return differenceInCalendarDays(date, new Date()) >= 7;
|
||||
if (value === "2_weeks") return differenceInCalendarDays(date, new Date()) >= 14;
|
||||
if (value === "1_months") return differenceInCalendarDays(date, new Date()) >= 30;
|
||||
if (value === "2_months") return differenceInCalendarDays(date, new Date()) >= 60;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import differenceInCalendarDays from "date-fns/differenceInCalendarDays";
|
||||
// helpers
|
||||
import { getDate } from "./date-time.helper";
|
||||
import { orderArrayBy } from "helpers/array.helper";
|
||||
// types
|
||||
import {
|
||||
@@ -157,7 +158,9 @@ export const shouldHighlightIssueDueDate = (
|
||||
// if the issue is completed or cancelled, don't highlight the due date
|
||||
if ([STATE_GROUPS.completed.key, STATE_GROUPS.cancelled.key].includes(stateGroup)) return false;
|
||||
|
||||
const parsedDate = new Date(date);
|
||||
const parsedDate = getDate(date);
|
||||
if (!parsedDate) return false;
|
||||
|
||||
const targetDateDistance = differenceInCalendarDays(parsedDate, new Date());
|
||||
|
||||
// if the issue is overdue, highlight the due date
|
||||
@@ -168,6 +171,6 @@ export const renderIssueBlocksStructure = (blocks: TIssue[]): IGanttBlock[] =>
|
||||
data: block,
|
||||
id: block.id,
|
||||
sort_order: block.sort_order,
|
||||
start_date: block.start_date ? new Date(block.start_date) : null,
|
||||
target_date: block.target_date ? new Date(block.target_date) : null,
|
||||
start_date: getDate(block.start_date),
|
||||
target_date: getDate(block.target_date),
|
||||
}));
|
||||
|
||||
@@ -21,6 +21,7 @@ import { DocumentEditorWithRef, DocumentReadOnlyEditorWithRef } from "@plane/doc
|
||||
import { Spinner } from "@plane/ui";
|
||||
// assets
|
||||
// helpers
|
||||
import { getDate } from "helpers/date-time.helper";
|
||||
// types
|
||||
import { IPage } from "@plane/types";
|
||||
import { NextPageWithLayout } from "lib/types";
|
||||
@@ -271,8 +272,8 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
|
||||
documentDetails={{
|
||||
title: pageTitle,
|
||||
created_by: created_by,
|
||||
created_on: created_at,
|
||||
last_updated_at: updated_at,
|
||||
created_on: getDate(created_at) ?? new Date(created_at ?? ""),
|
||||
last_updated_at: getDate(updated_at) ?? new Date(created_at ?? ""),
|
||||
last_updated_by: updated_by,
|
||||
}}
|
||||
pageLockConfig={userCanLock && !archived_at ? { action: unlockPage, is_locked: is_locked } : undefined}
|
||||
@@ -282,7 +283,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
|
||||
? {
|
||||
action: archived_at ? unArchivePage : archivePage,
|
||||
is_archived: archived_at ? true : false,
|
||||
archived_at: archived_at ? new Date(archived_at) : undefined,
|
||||
archived_at: getDate(archived_at),
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
@@ -298,8 +299,8 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
|
||||
documentDetails={{
|
||||
title: pageTitle,
|
||||
created_by: created_by,
|
||||
created_on: created_at,
|
||||
last_updated_at: updated_at,
|
||||
created_on: getDate(created_at) ?? new Date(created_at ?? ""),
|
||||
last_updated_at: getDate(updated_at) ?? new Date(created_at ?? ""),
|
||||
last_updated_by: updated_by,
|
||||
}}
|
||||
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
|
||||
|
||||
@@ -4,6 +4,10 @@ import { isFuture, isPast, isToday } from "date-fns";
|
||||
import set from "lodash/set";
|
||||
import sortBy from "lodash/sortBy";
|
||||
// types
|
||||
// helpers
|
||||
import { getDate } from "helpers/date-time.helper";
|
||||
// mobx
|
||||
// services
|
||||
import { ICycle, CycleDateCheckData } from "@plane/types";
|
||||
// mobx
|
||||
import { RootStore } from "store/root.store";
|
||||
@@ -118,8 +122,9 @@ export class CycleStore implements ICycleStore {
|
||||
const projectId = this.rootStore.app.router.projectId;
|
||||
if (!projectId || !this.fetchedMap[projectId]) return null;
|
||||
let completedCycles = Object.values(this.cycleMap ?? {}).filter((c) => {
|
||||
const hasEndDatePassed = isPast(new Date(c.end_date ?? ""));
|
||||
const isEndDateToday = isToday(new Date(c.end_date ?? ""));
|
||||
const endDate = getDate(c.end_date);
|
||||
const hasEndDatePassed = endDate && isPast(endDate);
|
||||
const isEndDateToday = endDate && isToday(endDate);
|
||||
return c.project_id === projectId && hasEndDatePassed && !isEndDateToday;
|
||||
});
|
||||
completedCycles = sortBy(completedCycles, [(c) => c.sort_order]);
|
||||
@@ -134,7 +139,8 @@ export class CycleStore implements ICycleStore {
|
||||
const projectId = this.rootStore.app.router.projectId;
|
||||
if (!projectId || !this.fetchedMap[projectId]) return null;
|
||||
let upcomingCycles = Object.values(this.cycleMap ?? {}).filter((c) => {
|
||||
const isStartDateUpcoming = isFuture(new Date(c.start_date ?? ""));
|
||||
const startDate = getDate(c.start_date);
|
||||
const isStartDateUpcoming = startDate && isFuture(startDate);
|
||||
return c.project_id === projectId && isStartDateUpcoming;
|
||||
});
|
||||
upcomingCycles = sortBy(upcomingCycles, [(c) => c.sort_order]);
|
||||
@@ -149,7 +155,8 @@ export class CycleStore implements ICycleStore {
|
||||
const projectId = this.rootStore.app.router.projectId;
|
||||
if (!projectId || !this.fetchedMap[projectId]) return null;
|
||||
let incompleteCycles = Object.values(this.cycleMap ?? {}).filter((c) => {
|
||||
const hasEndDatePassed = isPast(new Date(c.end_date ?? ""));
|
||||
const endDate = getDate(c.end_date);
|
||||
const hasEndDatePassed = endDate && isPast(endDate);
|
||||
return c.project_id === projectId && !hasEndDatePassed;
|
||||
});
|
||||
incompleteCycles = sortBy(incompleteCycles, [(c) => c.sort_order]);
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import orderBy from "lodash/orderBy";
|
||||
import get from "lodash/get";
|
||||
import indexOf from "lodash/indexOf";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
import orderBy from "lodash/orderBy";
|
||||
import values from "lodash/values";
|
||||
// types
|
||||
import { TIssue, TIssueMap, TIssueGroupByOptions, TIssueOrderByOptions } from "@plane/types";
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
// constants
|
||||
import { ISSUE_PRIORITIES } from "constants/issue";
|
||||
import { STATE_GROUPS } from "constants/state";
|
||||
// helpers
|
||||
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
// types
|
||||
import { TIssue, TIssueMap, TIssueGroupByOptions, TIssueOrderByOptions } from "@plane/types";
|
||||
// store
|
||||
import { IIssueRootStore } from "../root.store";
|
||||
|
||||
export type TIssueDisplayFilterOptions = Exclude<TIssueGroupByOptions, null> | "target_date";
|
||||
|
||||
@@ -62,35 +63,35 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
issues: TIssueMap,
|
||||
isCalendarIssues: boolean = false
|
||||
) => {
|
||||
const _issues: { [group_id: string]: string[] } = {};
|
||||
if (!groupBy) return _issues;
|
||||
const currentIssues: { [group_id: string]: string[] } = {};
|
||||
if (!groupBy) return currentIssues;
|
||||
|
||||
this.issueDisplayFiltersDefaultData(groupBy).forEach((group) => {
|
||||
_issues[group] = [];
|
||||
currentIssues[group] = [];
|
||||
});
|
||||
|
||||
const projectIssues = this.issuesSortWithOrderBy(issues, orderBy);
|
||||
|
||||
for (const issue in projectIssues) {
|
||||
const _issue = projectIssues[issue];
|
||||
const currentIssue = projectIssues[issue];
|
||||
let groupArray = [];
|
||||
|
||||
if (groupBy === "state_detail.group") {
|
||||
const state_group =
|
||||
this.rootStore?.stateDetails?.find((_state) => _state.id === _issue?.state_id)?.group || "None";
|
||||
// if groupBy state_detail.group is coming from the project level the we are using stateDetails from root store else we are looping through the stateMap
|
||||
const state_group = (this.rootStore?.stateMap || {})?.[currentIssue?.state_id]?.group || "None";
|
||||
groupArray = [state_group];
|
||||
} else {
|
||||
const groupValue = get(_issue, ISSUE_FILTER_DEFAULT_DATA[groupBy]);
|
||||
groupArray = groupValue !== undefined ? this.getGroupArray(groupValue, isCalendarIssues) : [];
|
||||
const groupValue = get(currentIssue, ISSUE_FILTER_DEFAULT_DATA[groupBy]);
|
||||
groupArray = groupValue !== undefined ? this.getGroupArray(groupValue, isCalendarIssues) : ["None"];
|
||||
}
|
||||
|
||||
for (const group of groupArray) {
|
||||
if (group && _issues[group]) _issues[group].push(_issue.id);
|
||||
else if (group) _issues[group] = [_issue.id];
|
||||
if (group && currentIssues[group]) currentIssues[group].push(currentIssue.id);
|
||||
else if (group) currentIssues[group] = [currentIssue.id];
|
||||
}
|
||||
}
|
||||
|
||||
return _issues;
|
||||
return currentIssues;
|
||||
};
|
||||
|
||||
subGroupedIssues = (
|
||||
@@ -99,45 +100,47 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
orderBy: TIssueOrderByOptions,
|
||||
issues: TIssueMap
|
||||
) => {
|
||||
const _issues: { [sub_group_id: string]: { [group_id: string]: string[] } } = {};
|
||||
if (!subGroupBy || !groupBy) return _issues;
|
||||
const currentIssues: { [sub_group_id: string]: { [group_id: string]: string[] } } = {};
|
||||
if (!subGroupBy || !groupBy) return currentIssues;
|
||||
|
||||
this.issueDisplayFiltersDefaultData(subGroupBy).forEach((sub_group: any) => {
|
||||
this.issueDisplayFiltersDefaultData(subGroupBy).forEach((sub_group) => {
|
||||
const groupByIssues: { [group_id: string]: string[] } = {};
|
||||
this.issueDisplayFiltersDefaultData(groupBy).forEach((group) => {
|
||||
groupByIssues[group] = [];
|
||||
});
|
||||
_issues[sub_group] = groupByIssues;
|
||||
currentIssues[sub_group] = groupByIssues;
|
||||
});
|
||||
|
||||
const projectIssues = this.issuesSortWithOrderBy(issues, orderBy);
|
||||
|
||||
for (const issue in projectIssues) {
|
||||
const _issue = projectIssues[issue];
|
||||
const currentIssue = projectIssues[issue];
|
||||
let subGroupArray = [];
|
||||
let groupArray = [];
|
||||
if (subGroupBy === "state_detail.group" || groupBy === "state_detail.group") {
|
||||
const state_group =
|
||||
this.rootStore?.stateDetails?.find((_state) => _state.id === _issue?.state_id)?.group || "None";
|
||||
const state_group = (this.rootStore?.stateMap || {})?.[currentIssue?.state_id]?.group || "None";
|
||||
|
||||
subGroupArray = [state_group];
|
||||
groupArray = [state_group];
|
||||
} else {
|
||||
const subGroupValue = get(_issue, ISSUE_FILTER_DEFAULT_DATA[subGroupBy]);
|
||||
const groupValue = get(_issue, ISSUE_FILTER_DEFAULT_DATA[groupBy]);
|
||||
subGroupArray = subGroupValue != undefined ? this.getGroupArray(subGroupValue) : [];
|
||||
groupArray = groupValue != undefined ? this.getGroupArray(groupValue) : [];
|
||||
const subGroupValue = get(currentIssue, ISSUE_FILTER_DEFAULT_DATA[subGroupBy]);
|
||||
const groupValue = get(currentIssue, ISSUE_FILTER_DEFAULT_DATA[groupBy]);
|
||||
|
||||
subGroupArray = subGroupValue != undefined ? this.getGroupArray(subGroupValue) : ["None"];
|
||||
groupArray = groupValue != undefined ? this.getGroupArray(groupValue) : ["None"];
|
||||
}
|
||||
|
||||
for (const subGroup of subGroupArray) {
|
||||
for (const group of groupArray) {
|
||||
if (subGroup && group && _issues?.[subGroup]?.[group]) _issues[subGroup][group].push(_issue.id);
|
||||
else if (subGroup && group && _issues[subGroup]) _issues[subGroup][group] = [_issue.id];
|
||||
else if (subGroup && group) _issues[subGroup] = { [group]: [_issue.id] };
|
||||
if (subGroup && group && currentIssues?.[subGroup]?.[group])
|
||||
currentIssues[subGroup][group].push(currentIssue.id);
|
||||
else if (subGroup && group && currentIssues[subGroup]) currentIssues[subGroup][group] = [currentIssue.id];
|
||||
else if (subGroup && group) currentIssues[subGroup] = { [group]: [currentIssue.id] };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _issues;
|
||||
return currentIssues;
|
||||
};
|
||||
|
||||
unGroupedIssues = (orderBy: TIssueOrderByOptions, issues: TIssueMap) =>
|
||||
@@ -215,8 +218,8 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
const moduleMap = this.rootStore?.moduleMap;
|
||||
if (!moduleMap) break;
|
||||
for (const dataId of dataIdsArray) {
|
||||
const _module = moduleMap[dataId];
|
||||
if (_module && _module.name) dataValues.push(_module.name.toLocaleLowerCase());
|
||||
const currentModule = moduleMap[dataId];
|
||||
if (currentModule && currentModule.name) dataValues.push(currentModule.name.toLocaleLowerCase());
|
||||
}
|
||||
break;
|
||||
case "cycle_id":
|
||||
@@ -233,10 +236,10 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* This Method is mainly used to filter out empty values in the begining
|
||||
* This Method is mainly used to filter out empty values in the beginning
|
||||
* @param key key of the value that is to be checked if empty
|
||||
* @param object any object in which the key's value is to be checked
|
||||
* @returns 1 if emoty, 0 if not empty
|
||||
* @returns 1 if empty, 0 if not empty
|
||||
*/
|
||||
getSortOrderToFilterEmptyValues(key: string, object: any) {
|
||||
const value = object?.[key];
|
||||
@@ -388,7 +391,7 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
getGroupArray(value: boolean | number | string | string[] | null, isDate: boolean = false): string[] {
|
||||
if (!value || value === null || value === undefined) return ["None"];
|
||||
if (Array.isArray(value))
|
||||
if (value.length) return value;
|
||||
if (value && value.length) return value;
|
||||
else return ["None"];
|
||||
else if (typeof value === "boolean") return [value ? "True" : "False"];
|
||||
else if (typeof value === "number") return [value.toString()];
|
||||
|
||||
@@ -10,7 +10,7 @@ export interface IPageStore {
|
||||
access: number;
|
||||
archived_at: string | null;
|
||||
color: string;
|
||||
created_at: Date;
|
||||
created_at: string | null;
|
||||
created_by: string;
|
||||
description: string;
|
||||
description_html: string;
|
||||
@@ -23,7 +23,7 @@ export interface IPageStore {
|
||||
name: string;
|
||||
owned_by: string;
|
||||
project: string;
|
||||
updated_at: Date;
|
||||
updated_at: string | null;
|
||||
updated_by: string;
|
||||
workspace: string;
|
||||
|
||||
@@ -52,7 +52,7 @@ export class PageStore implements IPageStore {
|
||||
isSubmitting: "submitting" | "submitted" | "saved" = "saved";
|
||||
archived_at: string | null;
|
||||
color: string;
|
||||
created_at: Date;
|
||||
created_at: string | null;
|
||||
created_by: string;
|
||||
description: string;
|
||||
description_html = "";
|
||||
@@ -64,7 +64,7 @@ export class PageStore implements IPageStore {
|
||||
name = "";
|
||||
owned_by: string;
|
||||
project: string;
|
||||
updated_at: Date;
|
||||
updated_at: string | null;
|
||||
updated_by: string;
|
||||
workspace: string;
|
||||
oldName = "";
|
||||
@@ -94,9 +94,9 @@ export class PageStore implements IPageStore {
|
||||
cleanup: action,
|
||||
});
|
||||
this.created_by = page?.created_by || "";
|
||||
this.created_at = page?.created_at || new Date();
|
||||
this.created_at = page?.created_at ?? "";
|
||||
this.color = page?.color || "";
|
||||
this.archived_at = page?.archived_at || null;
|
||||
this.archived_at = page?.archived_at ?? null;
|
||||
this.name = page?.name || "";
|
||||
this.description = page?.description || "";
|
||||
this.description_stripped = page?.description_stripped || "";
|
||||
@@ -104,7 +104,7 @@ export class PageStore implements IPageStore {
|
||||
this.access = page?.access || 0;
|
||||
this.workspace = page?.workspace || "";
|
||||
this.updated_by = page?.updated_by || "";
|
||||
this.updated_at = page?.updated_at || new Date();
|
||||
this.updated_at = page?.updated_at ?? "";
|
||||
this.project = page?.project || "";
|
||||
this.owned_by = page?.owned_by || "";
|
||||
this.labels = page?.labels || [];
|
||||
|
||||
@@ -8,6 +8,7 @@ import { PageStore, IPageStore } from "store/page.store";
|
||||
import { IPage, IRecentPages } from "@plane/types";
|
||||
import { RootStore } from "./root.store";
|
||||
import { isThisWeek, isToday, isYesterday } from "date-fns";
|
||||
import { getDate } from "helpers/date-time.helper";
|
||||
|
||||
export interface IProjectPageStore {
|
||||
loader: boolean;
|
||||
@@ -73,8 +74,8 @@ export class ProjectPageStore implements IProjectPageStore {
|
||||
|
||||
const allProjectIds = Object.keys(this.projectPageMap[projectId]);
|
||||
return allProjectIds.sort((a, b) => {
|
||||
const dateA = new Date(this.projectPageMap[projectId][a].created_at).getTime();
|
||||
const dateB = new Date(this.projectPageMap[projectId][b].created_at).getTime();
|
||||
const dateA = getDate(this.projectPageMap[projectId]?.[a]?.created_at)?.getTime() ?? 0;
|
||||
const dateB = getDate(this.projectPageMap[projectId]?.[b]?.created_at)?.getTime() ?? 0;
|
||||
return dateB - dateA;
|
||||
});
|
||||
}
|
||||
@@ -84,8 +85,8 @@ export class ProjectPageStore implements IProjectPageStore {
|
||||
if (!projectId || !this.projectArchivedPageMap[projectId]) return [];
|
||||
const archivedPages = Object.keys(this.projectArchivedPageMap[projectId]);
|
||||
return archivedPages.sort((a, b) => {
|
||||
const dateA = new Date(this.projectArchivedPageMap[projectId][a].created_at).getTime();
|
||||
const dateB = new Date(this.projectArchivedPageMap[projectId][b].created_at).getTime();
|
||||
const dateA = getDate(this.projectArchivedPageMap[projectId]?.[a]?.created_at)?.getTime() ?? 0;
|
||||
const dateB = getDate(this.projectArchivedPageMap[projectId]?.[b]?.created_at)?.getTime() ?? 0;
|
||||
return dateB - dateA;
|
||||
});
|
||||
}
|
||||
@@ -126,26 +127,24 @@ export class ProjectPageStore implements IProjectPageStore {
|
||||
const projectId = this.rootStore.app.router.projectId;
|
||||
if (!this.projectPageIds || !projectId) return;
|
||||
|
||||
const today: string[] = this.projectPageIds.filter((page) =>
|
||||
isToday(new Date(this.projectPageMap[projectId][page].updated_at))
|
||||
);
|
||||
const today: string[] = this.projectPageIds.filter((page) => {
|
||||
const updatedAt = getDate(this.projectPageMap[projectId]?.[page]?.updated_at);
|
||||
return updatedAt && isToday(updatedAt);
|
||||
});
|
||||
|
||||
const yesterday: string[] = this.projectPageIds.filter((page) =>
|
||||
isYesterday(new Date(this.projectPageMap[projectId][page].updated_at))
|
||||
);
|
||||
const yesterday: string[] = this.projectPageIds.filter((page) => {
|
||||
const updatedAt = getDate(this.projectPageMap[projectId]?.[page]?.updated_at);
|
||||
return updatedAt && isYesterday(updatedAt);
|
||||
});
|
||||
|
||||
const this_week: string[] = this.projectPageIds.filter((page) => {
|
||||
const pageUpdatedAt = this.projectPageMap[projectId][page].updated_at;
|
||||
return (
|
||||
isThisWeek(new Date(pageUpdatedAt)) &&
|
||||
!isToday(new Date(pageUpdatedAt)) &&
|
||||
!isYesterday(new Date(pageUpdatedAt))
|
||||
);
|
||||
const pageUpdatedAt = getDate(this.projectPageMap[projectId]?.[page]?.updated_at);
|
||||
return pageUpdatedAt && isThisWeek(pageUpdatedAt) && !isToday(pageUpdatedAt) && !isYesterday(pageUpdatedAt);
|
||||
});
|
||||
|
||||
const older: string[] = this.projectPageIds.filter((page) => {
|
||||
const pageUpdatedAt = this.projectPageMap[projectId][page].updated_at;
|
||||
return !isThisWeek(new Date(pageUpdatedAt)) && !isYesterday(new Date(pageUpdatedAt));
|
||||
const pageUpdatedAt = getDate(this.projectPageMap[projectId]?.[page]?.updated_at);
|
||||
return pageUpdatedAt && !isThisWeek(pageUpdatedAt) && !isYesterday(pageUpdatedAt);
|
||||
});
|
||||
|
||||
return { today, yesterday, this_week, older };
|
||||
|
||||
Reference in New Issue
Block a user