mirror of
https://github.com/makeplane/plane.git
synced 2026-02-24 04:00:14 +01:00
Merge branch 'sync/ce-ee' of github.com:makeplane/plane-ee into develop
This commit is contained in:
@@ -148,10 +148,12 @@ def send_email_notification(
|
||||
template_data = []
|
||||
total_changes = 0
|
||||
comments = []
|
||||
actors_involved = []
|
||||
for actor_id, changes in data.items():
|
||||
actor = User.objects.get(pk=actor_id)
|
||||
total_changes = total_changes + len(changes)
|
||||
comment = changes.pop("comment", False)
|
||||
actors_involved.append(actor_id)
|
||||
if comment:
|
||||
comments.append(
|
||||
{
|
||||
@@ -191,6 +193,7 @@ def send_email_notification(
|
||||
context = {
|
||||
"data": template_data,
|
||||
"summary": summary,
|
||||
"actors_involved": len(set(actors_involved)),
|
||||
"issue": {
|
||||
"issue_identifier": f"{str(issue.project.identifier)}-{str(issue.sequence_id)}",
|
||||
"name": issue.name,
|
||||
@@ -200,6 +203,9 @@ def send_email_notification(
|
||||
"email": receiver.email,
|
||||
},
|
||||
"issue_url": f"{base_api}/{str(issue.project.workspace.slug)}/projects/{str(issue.project.id)}/issues/{str(issue.id)}",
|
||||
"project_url": f"{base_api}/{str(issue.project.workspace.slug)}/projects/{str(issue.project.id)}/issues/",
|
||||
"workspace":str(issue.project.workspace.slug),
|
||||
"project": str(issue.project.name),
|
||||
"user_preference": f"{base_api}/profile/preferences/email",
|
||||
"comments": comments,
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,8 @@ import useToast from "hooks/use-toast";
|
||||
import { Button, Input } from "@plane/ui";
|
||||
// helpers
|
||||
import { checkEmailValidity } from "helpers/string.helper";
|
||||
// icons
|
||||
import { Eye, EyeOff } from "lucide-react";
|
||||
|
||||
type Props = {
|
||||
email: string;
|
||||
@@ -31,6 +33,7 @@ export const SignInOptionalSetPasswordForm: React.FC<Props> = (props) => {
|
||||
const { email, handleSignInRedirection } = props;
|
||||
// states
|
||||
const [isGoingToWorkspace, setIsGoingToWorkspace] = useState(false);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
// toast alert
|
||||
const { setToastAlert } = useToast();
|
||||
// form info
|
||||
@@ -114,17 +117,30 @@ export const SignInOptionalSetPasswordForm: React.FC<Props> = (props) => {
|
||||
required: "Password is required",
|
||||
}}
|
||||
render={({ field: { value, onChange, ref } }) => (
|
||||
<Input
|
||||
type="password"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
ref={ref}
|
||||
hasError={Boolean(errors.password)}
|
||||
placeholder="Enter password"
|
||||
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
||||
minLength={8}
|
||||
autoFocus
|
||||
/>
|
||||
<div className="relative flex items-center rounded-md bg-onboarding-background-200">
|
||||
<Input
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
ref={ref}
|
||||
hasError={Boolean(errors.password)}
|
||||
placeholder="Enter password"
|
||||
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
||||
minLength={8}
|
||||
autoFocus
|
||||
/>
|
||||
{showPassword ? (
|
||||
<EyeOff
|
||||
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
|
||||
onClick={() => setShowPassword(false)}
|
||||
/>
|
||||
) : (
|
||||
<Eye
|
||||
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
|
||||
onClick={() => setShowPassword(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<p className="text-onboarding-text-200 text-xs mt-2 pb-3">
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { XCircle } from "lucide-react";
|
||||
import { Eye, EyeOff, XCircle } from "lucide-react";
|
||||
// services
|
||||
import { AuthService } from "services/auth.service";
|
||||
// hooks
|
||||
@@ -40,6 +40,7 @@ export const SignInPasswordForm: React.FC<Props> = observer((props) => {
|
||||
const { email, handleStepChange, handleEmailClear, onSubmit } = props;
|
||||
// states
|
||||
const [isSendingUniqueCode, setIsSendingUniqueCode] = useState(false);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
// toast alert
|
||||
const { setToastAlert } = useToast();
|
||||
const {
|
||||
@@ -157,15 +158,28 @@ export const SignInPasswordForm: React.FC<Props> = observer((props) => {
|
||||
required: "Password is required",
|
||||
}}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Input
|
||||
type="password"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
hasError={Boolean(errors.password)}
|
||||
placeholder="Enter password"
|
||||
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
||||
autoFocus
|
||||
/>
|
||||
<div className="relative flex items-center rounded-md bg-onboarding-background-200">
|
||||
<Input
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
hasError={Boolean(errors.password)}
|
||||
placeholder="Enter password"
|
||||
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
||||
autoFocus
|
||||
/>
|
||||
{showPassword ? (
|
||||
<EyeOff
|
||||
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
|
||||
onClick={() => setShowPassword(false)}
|
||||
/>
|
||||
) : (
|
||||
<Eye
|
||||
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
|
||||
onClick={() => setShowPassword(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<div className="w-full text-right mt-2 pb-3">
|
||||
|
||||
@@ -10,6 +10,8 @@ import { Button, Input } from "@plane/ui";
|
||||
import { checkEmailValidity } from "helpers/string.helper";
|
||||
// constants
|
||||
import { ESignUpSteps } from "components/account";
|
||||
// icons
|
||||
import { Eye, EyeOff } from "lucide-react";
|
||||
|
||||
type Props = {
|
||||
email: string;
|
||||
@@ -34,6 +36,7 @@ export const SignUpOptionalSetPasswordForm: React.FC<Props> = (props) => {
|
||||
const { email, handleSignInRedirection } = props;
|
||||
// states
|
||||
const [isGoingToWorkspace, setIsGoingToWorkspace] = useState(false);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
// toast alert
|
||||
const { setToastAlert } = useToast();
|
||||
// form info
|
||||
@@ -119,16 +122,29 @@ export const SignUpOptionalSetPasswordForm: React.FC<Props> = (props) => {
|
||||
required: "Password is required",
|
||||
}}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Input
|
||||
type="password"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
hasError={Boolean(errors.password)}
|
||||
placeholder="Enter password"
|
||||
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
||||
minLength={8}
|
||||
autoFocus
|
||||
/>
|
||||
<div className="relative flex items-center rounded-md bg-onboarding-background-200">
|
||||
<Input
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
hasError={Boolean(errors.password)}
|
||||
placeholder="Enter password"
|
||||
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
||||
minLength={8}
|
||||
autoFocus
|
||||
/>
|
||||
{showPassword ? (
|
||||
<EyeOff
|
||||
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
|
||||
onClick={() => setShowPassword(false)}
|
||||
/>
|
||||
) : (
|
||||
<Eye
|
||||
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
|
||||
onClick={() => setShowPassword(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<p className="text-onboarding-text-200 text-xs mt-2 pb-3">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { XCircle } from "lucide-react";
|
||||
import { Eye, EyeOff, XCircle } from "lucide-react";
|
||||
// services
|
||||
import { AuthService } from "services/auth.service";
|
||||
// hooks
|
||||
@@ -32,6 +32,8 @@ const authService = new AuthService();
|
||||
|
||||
export const SignUpPasswordForm: React.FC<Props> = observer((props) => {
|
||||
const { onSubmit } = props;
|
||||
// states
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
// toast alert
|
||||
const { setToastAlert } = useToast();
|
||||
// form info
|
||||
@@ -112,15 +114,28 @@ export const SignUpPasswordForm: React.FC<Props> = observer((props) => {
|
||||
required: "Password is required",
|
||||
}}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Input
|
||||
type="password"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
hasError={Boolean(errors.password)}
|
||||
placeholder="Enter password"
|
||||
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
||||
autoFocus
|
||||
/>
|
||||
<div className="relative flex items-center rounded-md bg-onboarding-background-200">
|
||||
<Input
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
hasError={Boolean(errors.password)}
|
||||
placeholder="Enter password"
|
||||
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
||||
autoFocus
|
||||
/>
|
||||
{showPassword ? (
|
||||
<EyeOff
|
||||
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
|
||||
onClick={() => setShowPassword(false)}
|
||||
/>
|
||||
) : (
|
||||
<Eye
|
||||
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
|
||||
onClick={() => setShowPassword(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<p className="text-onboarding-text-200 text-xs mt-2 pb-3">
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { FC } from "react";
|
||||
// hooks
|
||||
import { useIssueDetail } from "hooks/store";
|
||||
import { useChart } from "../hooks";
|
||||
// helpers
|
||||
import { ChartAddBlock, ChartDraggable } from "components/gantt-chart";
|
||||
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
import { cn } from "helpers/common.helper";
|
||||
// types
|
||||
import { IBlockUpdateData, IGanttBlock } from "../types";
|
||||
|
||||
@@ -31,6 +33,7 @@ export const GanttChartBlocks: FC<GanttChartBlocksProps> = (props) => {
|
||||
} = props;
|
||||
|
||||
const { activeBlock, dispatch } = useChart();
|
||||
const { peekIssue } = useIssueDetail();
|
||||
|
||||
// update the active block on hover
|
||||
const updateActiveBlock = (block: IGanttBlock | null) => {
|
||||
@@ -88,7 +91,14 @@ export const GanttChartBlocks: FC<GanttChartBlocksProps> = (props) => {
|
||||
return (
|
||||
<div
|
||||
key={`block-${block.id}`}
|
||||
className={`h-11 ${activeBlock?.id === block.id ? "bg-custom-background-80" : ""}`}
|
||||
className={cn(
|
||||
"h-11",
|
||||
{ "rounded bg-custom-background-80": activeBlock?.id === block.id },
|
||||
{
|
||||
"rounded-l border border-r-0 border-custom-primary-70 hover:border-custom-primary-70":
|
||||
peekIssue?.issueId === block.data.id,
|
||||
}
|
||||
)}
|
||||
onMouseEnter={() => updateActiveBlock(block)}
|
||||
onMouseLeave={() => updateActiveBlock(null)}
|
||||
>
|
||||
|
||||
@@ -3,12 +3,14 @@ import { DragDropContext, Draggable, Droppable, DropResult } from "@hello-pangea
|
||||
import { MoreVertical } from "lucide-react";
|
||||
// hooks
|
||||
import { useChart } from "components/gantt-chart/hooks";
|
||||
import { useIssueDetail } from "hooks/store";
|
||||
// ui
|
||||
import { Loader } from "@plane/ui";
|
||||
// components
|
||||
import { GanttQuickAddIssueForm, IssueGanttSidebarBlock } from "components/issues";
|
||||
// helpers
|
||||
import { findTotalDaysInRange } from "helpers/date-time.helper";
|
||||
import { cn } from "helpers/common.helper";
|
||||
// types
|
||||
import { IGanttBlock, IBlockUpdateData } from "components/gantt-chart/types";
|
||||
import { TIssue } from "@plane/types";
|
||||
@@ -45,6 +47,7 @@ export const IssueGanttSidebar: React.FC<Props> = (props) => {
|
||||
const { cycleId } = router.query;
|
||||
|
||||
const { activeBlock, dispatch } = useChart();
|
||||
const { peekIssue } = useIssueDetail();
|
||||
|
||||
// update the active block on hover
|
||||
const updateActiveBlock = (block: IGanttBlock | null) => {
|
||||
@@ -104,7 +107,7 @@ export const IssueGanttSidebar: React.FC<Props> = (props) => {
|
||||
{(droppableProvided) => (
|
||||
<div
|
||||
id={`gantt-sidebar-${cycleId}`}
|
||||
className="mt-3 max-h-full overflow-y-auto pl-2.5"
|
||||
className="mt-[12px] max-h-full overflow-y-auto pl-2.5"
|
||||
ref={droppableProvided.innerRef}
|
||||
{...droppableProvided.droppableProps}
|
||||
>
|
||||
@@ -130,7 +133,14 @@ export const IssueGanttSidebar: React.FC<Props> = (props) => {
|
||||
>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
className={`h-11 ${snapshot.isDragging ? "rounded bg-custom-background-80" : ""}`}
|
||||
className={cn(
|
||||
"h-11",
|
||||
{ "rounded bg-custom-background-80": snapshot.isDragging },
|
||||
{
|
||||
"rounded-l border border-r-0 border-custom-primary-70 hover:border-custom-primary-70":
|
||||
peekIssue?.issueId === block.data.id,
|
||||
}
|
||||
)}
|
||||
onMouseEnter={() => updateActiveBlock(block)}
|
||||
onMouseLeave={() => updateActiveBlock(null)}
|
||||
ref={provided.innerRef}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FC } from "react";
|
||||
import { FC, useState } from "react";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { XCircle } from "lucide-react";
|
||||
import { Eye, EyeOff, XCircle } from "lucide-react";
|
||||
// hooks
|
||||
import { useUser } from "hooks/store";
|
||||
// ui
|
||||
@@ -24,6 +24,8 @@ export interface IInstanceSetupEmailForm {
|
||||
|
||||
export const InstanceSetupSignInForm: FC<IInstanceSetupEmailForm> = (props) => {
|
||||
const { handleNextStep } = props;
|
||||
// states
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
// store hooks
|
||||
const { fetchCurrentUser } = useUser();
|
||||
// form info
|
||||
@@ -107,14 +109,27 @@ export const InstanceSetupSignInForm: FC<IInstanceSetupEmailForm> = (props) => {
|
||||
required: "Password is required",
|
||||
}}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Input
|
||||
type="password"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
hasError={Boolean(errors.password)}
|
||||
placeholder="Enter password"
|
||||
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
||||
/>
|
||||
<div className="relative flex items-center rounded-md bg-onboarding-background-200">
|
||||
<Input
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
hasError={Boolean(errors.password)}
|
||||
placeholder="Enter password"
|
||||
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
||||
/>
|
||||
{showPassword ? (
|
||||
<EyeOff
|
||||
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
|
||||
onClick={() => setShowPassword(false)}
|
||||
/>
|
||||
) : (
|
||||
<Eye
|
||||
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
|
||||
onClick={() => setShowPassword(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<p className="pb-2 text-xs text-custom-text-200">
|
||||
|
||||
@@ -79,7 +79,7 @@ export const InboxIssueMainContent: React.FC<Props> = observer((props) => {
|
||||
</div>
|
||||
|
||||
<div className="pb-12">
|
||||
<IssueActivity workspaceSlug={workspaceSlug} projectId={projectId} issueId={issueId} disabled={!is_editable} />
|
||||
<IssueActivity workspaceSlug={workspaceSlug} projectId={projectId} issueId={issueId} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -17,7 +17,6 @@ const fileService = new FileService();
|
||||
type TIssueCommentCreate = {
|
||||
workspaceSlug: string;
|
||||
activityOperations: TActivityOperations;
|
||||
disabled: boolean;
|
||||
showAccessSpecifier?: boolean;
|
||||
};
|
||||
|
||||
@@ -40,7 +39,7 @@ const commentAccess: commentAccessType[] = [
|
||||
];
|
||||
|
||||
export const IssueCommentCreate: FC<TIssueCommentCreate> = (props) => {
|
||||
const { workspaceSlug, activityOperations, disabled, showAccessSpecifier = false } = props;
|
||||
const { workspaceSlug, activityOperations, showAccessSpecifier = false } = props;
|
||||
const workspaceStore = useWorkspace();
|
||||
const workspaceId = workspaceStore.getWorkspaceBySlug(workspaceSlug as string)?.id as string;
|
||||
|
||||
@@ -94,7 +93,7 @@ export const IssueCommentCreate: FC<TIssueCommentCreate> = (props) => {
|
||||
}
|
||||
submitButton={
|
||||
<Button
|
||||
disabled={isSubmitting || disabled}
|
||||
disabled={isSubmitting}
|
||||
variant="primary"
|
||||
type="submit"
|
||||
className="!px-2.5 !py-1.5 !text-xs"
|
||||
|
||||
@@ -13,7 +13,6 @@ type TIssueActivity = {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
issueId: string;
|
||||
disabled: boolean;
|
||||
};
|
||||
|
||||
type TActivityTabs = "all" | "activity" | "comments";
|
||||
@@ -43,7 +42,7 @@ export type TActivityOperations = {
|
||||
};
|
||||
|
||||
export const IssueActivity: FC<TIssueActivity> = observer((props) => {
|
||||
const { workspaceSlug, projectId, issueId, disabled } = props;
|
||||
const { workspaceSlug, projectId, issueId } = props;
|
||||
// hooks
|
||||
const { createComment, updateComment, removeComment } = useIssueDetail();
|
||||
const { setToastAlert } = useToast();
|
||||
@@ -147,14 +146,11 @@ export const IssueActivity: FC<TIssueActivity> = observer((props) => {
|
||||
activityOperations={activityOperations}
|
||||
showAccessSpecifier={project.is_deployed}
|
||||
/>
|
||||
{!disabled && (
|
||||
<IssueCommentCreate
|
||||
workspaceSlug={workspaceSlug}
|
||||
activityOperations={activityOperations}
|
||||
disabled={disabled}
|
||||
showAccessSpecifier={project.is_deployed}
|
||||
/>
|
||||
)}
|
||||
<IssueCommentCreate
|
||||
workspaceSlug={workspaceSlug}
|
||||
activityOperations={activityOperations}
|
||||
showAccessSpecifier={project.is_deployed}
|
||||
/>
|
||||
</div>
|
||||
) : activityTab === "activity" ? (
|
||||
<IssueActivityRoot issueId={issueId} />
|
||||
@@ -166,14 +162,11 @@ export const IssueActivity: FC<TIssueActivity> = observer((props) => {
|
||||
activityOperations={activityOperations}
|
||||
showAccessSpecifier={project.is_deployed}
|
||||
/>
|
||||
{!disabled && (
|
||||
<IssueCommentCreate
|
||||
workspaceSlug={workspaceSlug}
|
||||
activityOperations={activityOperations}
|
||||
disabled={disabled}
|
||||
showAccessSpecifier={project.is_deployed}
|
||||
/>
|
||||
)}
|
||||
<IssueCommentCreate
|
||||
workspaceSlug={workspaceSlug}
|
||||
activityOperations={activityOperations}
|
||||
showAccessSpecifier={project.is_deployed}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -99,7 +99,7 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
|
||||
disabled={!is_editable}
|
||||
/>
|
||||
|
||||
<IssueActivity workspaceSlug={workspaceSlug} projectId={projectId} issueId={issueId} disabled={!is_editable} />
|
||||
<IssueActivity workspaceSlug={workspaceSlug} projectId={projectId} issueId={issueId} />
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -6,7 +6,8 @@ import { MoreHorizontal } from "lucide-react";
|
||||
import { Tooltip, ControlLink } from "@plane/ui";
|
||||
// hooks
|
||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||
// ui
|
||||
// helpers
|
||||
import { cn } from "helpers/common.helper";
|
||||
// types
|
||||
import { TIssue, TIssueMap } from "@plane/types";
|
||||
import { useApplication, useIssueDetail, useProject, useProjectState } from "hooks/store";
|
||||
@@ -26,7 +27,7 @@ export const CalendarIssueBlocks: React.FC<Props> = observer((props) => {
|
||||
} = useApplication();
|
||||
const { getProjectById } = useProject();
|
||||
const { getProjectStates } = useProjectState();
|
||||
const { setPeekIssue } = useIssueDetail();
|
||||
const { peekIssue, setPeekIssue } = useIssueDetail();
|
||||
// states
|
||||
const [isMenuActive, setIsMenuActive] = useState(false);
|
||||
|
||||
@@ -84,11 +85,18 @@ export const CalendarIssueBlocks: React.FC<Props> = observer((props) => {
|
||||
)}
|
||||
|
||||
<div
|
||||
className={`group/calendar-block flex h-8 w-full items-center justify-between gap-1.5 rounded border-[0.5px] border-custom-border-100 px-1 py-1.5 shadow-custom-shadow-2xs ${
|
||||
snapshot.isDragging
|
||||
? "bg-custom-background-90 shadow-custom-shadow-rg"
|
||||
: "bg-custom-background-100 hover:bg-custom-background-90"
|
||||
}`}
|
||||
className={cn(
|
||||
"group/calendar-block flex h-8 w-full items-center justify-between gap-1.5 rounded border-[0.5px] border-custom-border-200 hover:border-custom-border-400 px-1 py-1.5 ",
|
||||
{
|
||||
"bg-custom-background-90 shadow-custom-shadow-rg border-custom-primary-100":
|
||||
snapshot.isDragging,
|
||||
},
|
||||
{ "bg-custom-background-100 hover:bg-custom-background-90": !snapshot.isDragging },
|
||||
{
|
||||
"border border-custom-primary-70 hover:border-custom-primary-70":
|
||||
peekIssue?.issueId === issue.id,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div className="flex h-full items-center gap-1.5">
|
||||
<span
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { memo } from "react";
|
||||
import { Draggable, DraggableProvided, DraggableStateSnapshot } from "@hello-pangea/dnd";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// hooks
|
||||
import { useApplication, useIssueDetail, useProject } from "hooks/store";
|
||||
// components
|
||||
import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC";
|
||||
import { IssueProperties } from "../properties/all-properties";
|
||||
@@ -9,9 +11,11 @@ import { Tooltip, ControlLink } from "@plane/ui";
|
||||
// types
|
||||
import { TIssue, IIssueDisplayProperties, IIssueMap } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
import { useApplication, useIssueDetail, useProject } from "hooks/store";
|
||||
// helper
|
||||
import { cn } from "helpers/common.helper";
|
||||
|
||||
interface IssueBlockProps {
|
||||
peekIssueId?: string;
|
||||
issueId: string;
|
||||
issuesMap: IIssueMap;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
@@ -86,6 +90,7 @@ const KanbanIssueDetailsBlock: React.FC<IssueDetailsBlockProps> = observer((prop
|
||||
|
||||
export const KanbanIssueBlock: React.FC<IssueBlockProps> = memo((props) => {
|
||||
const {
|
||||
peekIssueId,
|
||||
issueId,
|
||||
issuesMap,
|
||||
displayProperties,
|
||||
@@ -121,9 +126,12 @@ export const KanbanIssueBlock: React.FC<IssueBlockProps> = memo((props) => {
|
||||
<div className="absolute left-0 top-0 z-[99999] h-full w-full animate-pulse bg-custom-background-100/20" />
|
||||
)}
|
||||
<div
|
||||
className={`space-y-2 rounded border-[0.5px] border-custom-border-200 bg-custom-background-100 px-3 py-2 text-sm shadow-custom-shadow-2xs transition-all ${
|
||||
isDragDisabled ? "" : "hover:cursor-grab"
|
||||
} ${snapshot.isDragging ? `border-custom-primary-100` : `border-transparent`}`}
|
||||
className={cn(
|
||||
"space-y-2 rounded border-[0.5px] border-custom-border-200 bg-custom-background-100 px-3 py-2 text-sm transition-all hover:border-custom-border-400",
|
||||
{ "hover:cursor-grab": !isDragDisabled },
|
||||
{ "border-custom-primary-100": snapshot.isDragging },
|
||||
{ "border border-custom-primary-70 hover:border-custom-primary-70": peekIssueId === issue.id }
|
||||
)}
|
||||
>
|
||||
<KanbanIssueDetailsBlock
|
||||
issue={issue}
|
||||
|
||||
@@ -9,6 +9,7 @@ interface IssueBlocksListProps {
|
||||
sub_group_id: string;
|
||||
columnId: string;
|
||||
issuesMap: IIssueMap;
|
||||
peekIssueId?: string;
|
||||
issueIds: string[];
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
isDragDisabled: boolean;
|
||||
@@ -22,6 +23,7 @@ const KanbanIssueBlocksListMemo: React.FC<IssueBlocksListProps> = (props) => {
|
||||
sub_group_id,
|
||||
columnId,
|
||||
issuesMap,
|
||||
peekIssueId,
|
||||
issueIds,
|
||||
displayProperties,
|
||||
isDragDisabled,
|
||||
@@ -44,6 +46,7 @@ const KanbanIssueBlocksListMemo: React.FC<IssueBlocksListProps> = (props) => {
|
||||
return (
|
||||
<KanbanIssueBlock
|
||||
key={draggableId}
|
||||
peekIssueId={peekIssueId}
|
||||
issueId={issueId}
|
||||
issuesMap={issuesMap}
|
||||
displayProperties={displayProperties}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { observer } from "mobx-react-lite";
|
||||
// hooks
|
||||
import { useKanbanView, useLabel, useMember, useProject, useProjectState } from "hooks/store";
|
||||
import { useIssueDetail, useKanbanView, useLabel, useMember, useProject, useProjectState } from "hooks/store";
|
||||
// components
|
||||
import { HeaderGroupByCard } from "./headers/group-by-card";
|
||||
import { KanbanGroup } from "./kanban-group";
|
||||
@@ -73,6 +73,7 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
||||
const project = useProject();
|
||||
const label = useLabel();
|
||||
const projectState = useProjectState();
|
||||
const { peekIssue } = useIssueDetail();
|
||||
|
||||
const list = getGroupByColumns(group_by as GroupByColumnTypes, project, label, projectState, member);
|
||||
|
||||
@@ -120,6 +121,7 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
||||
groupId={_list.id}
|
||||
issuesMap={issuesMap}
|
||||
issueIds={issueIds}
|
||||
peekIssueId={peekIssue?.issueId ?? ""}
|
||||
displayProperties={displayProperties}
|
||||
sub_group_by={sub_group_by}
|
||||
group_by={group_by}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { EIssueActions } from "../types";
|
||||
interface IKanbanGroup {
|
||||
groupId: string;
|
||||
issuesMap: IIssueMap;
|
||||
peekIssueId?: string;
|
||||
issueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
sub_group_by: string | null;
|
||||
@@ -47,6 +48,7 @@ export const KanbanGroup = (props: IKanbanGroup) => {
|
||||
issuesMap,
|
||||
displayProperties,
|
||||
issueIds,
|
||||
peekIssueId,
|
||||
isDragDisabled,
|
||||
handleIssues,
|
||||
quickActions,
|
||||
@@ -118,6 +120,7 @@ export const KanbanGroup = (props: IKanbanGroup) => {
|
||||
sub_group_id={sub_group_id}
|
||||
columnId={groupId}
|
||||
issuesMap={issuesMap}
|
||||
peekIssueId={peekIssueId}
|
||||
issueIds={(issueIds as TGroupedIssues)?.[groupId] || []}
|
||||
displayProperties={displayProperties}
|
||||
isDragDisabled={isDragDisabled}
|
||||
|
||||
@@ -5,6 +5,8 @@ import { IssueProperties } from "../properties/all-properties";
|
||||
import { useApplication, useIssueDetail, useProject } from "hooks/store";
|
||||
// ui
|
||||
import { Spinner, Tooltip, ControlLink } from "@plane/ui";
|
||||
// helper
|
||||
import { cn } from "helpers/common.helper";
|
||||
// types
|
||||
import { TIssue, IIssueDisplayProperties, TIssueMap } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
@@ -25,7 +27,7 @@ export const IssueBlock: React.FC<IssueBlockProps> = observer((props: IssueBlock
|
||||
router: { workspaceSlug, projectId },
|
||||
} = useApplication();
|
||||
const { getProjectById } = useProject();
|
||||
const { setPeekIssue } = useIssueDetail();
|
||||
const { peekIssue, setPeekIssue } = useIssueDetail();
|
||||
|
||||
const updateIssue = (issueToUpdate: TIssue) => {
|
||||
handleIssues(issueToUpdate, EIssueActions.UPDATE);
|
||||
@@ -47,7 +49,15 @@ export const IssueBlock: React.FC<IssueBlockProps> = observer((props: IssueBlock
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative flex items-center gap-3 bg-custom-background-100 p-3 text-sm">
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex items-center gap-3 bg-custom-background-100 p-3 text-sm border border-transparent border-b-custom-border-200 last:border-b-transparent",
|
||||
{
|
||||
"border border-custom-primary-70 hover:border-custom-primary-70":
|
||||
peekIssue && peekIssue.issueId === issue.id,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{displayProperties && displayProperties?.key && (
|
||||
<div className="flex-shrink-0 text-xs font-medium text-custom-text-300">
|
||||
{projectDetails?.identifier}-{issue.sequence_id}
|
||||
|
||||
@@ -18,7 +18,7 @@ export const IssueBlocksList: FC<Props> = (props) => {
|
||||
const { issueIds, issuesMap, handleIssues, quickActions, displayProperties, canEditProperties } = props;
|
||||
|
||||
return (
|
||||
<div className="relative h-full w-full divide-y-[0.5px] divide-custom-border-200">
|
||||
<div className="relative h-full w-full">
|
||||
{issueIds && issueIds.length > 0 ? (
|
||||
issueIds.map((issueId: string) => {
|
||||
if (!issueId) return null;
|
||||
|
||||
@@ -12,6 +12,8 @@ import { ControlLink, Tooltip } from "@plane/ui";
|
||||
// hooks
|
||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||
import { useIssueDetail, useProject } from "hooks/store";
|
||||
// helper
|
||||
import { cn } from "helpers/common.helper";
|
||||
// types
|
||||
import { IIssueDisplayProperties, TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
@@ -48,7 +50,7 @@ export const SpreadsheetIssueRow = observer((props: Props) => {
|
||||
const { workspaceSlug } = router.query;
|
||||
//hooks
|
||||
const { getProjectById } = useProject();
|
||||
const { setPeekIssue } = useIssueDetail();
|
||||
const { peekIssue, setPeekIssue } = useIssueDetail();
|
||||
// states
|
||||
const [isMenuActive, setIsMenuActive] = useState(false);
|
||||
const [isExpanded, setExpanded] = useState<boolean>(false);
|
||||
@@ -95,9 +97,20 @@ export const SpreadsheetIssueRow = observer((props: Props) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<tr>
|
||||
<tr
|
||||
className={cn({
|
||||
"border border-custom-primary-70 hover:border-custom-primary-70": peekIssue?.issueId === issueDetail.id,
|
||||
})}
|
||||
>
|
||||
{/* first column/ issue name and key column */}
|
||||
<td className="sticky group left-0 h-11 w-[28rem] flex items-center bg-custom-background-100 text-sm after:absolute after:w-full after:bottom-[-1px] after:border after:border-l-0 after:border-custom-border-100 before:absolute before:h-full before:right-0 before:border before:border-l-0 before:border-custom-border-100">
|
||||
<td
|
||||
className={cn(
|
||||
"sticky group left-0 h-11 w-[28rem] flex items-center bg-custom-background-100 text-sm after:absolute border-r-[0.5px] border-custom-border-200",
|
||||
{
|
||||
"border-b-[0.5px]": peekIssue?.issueId !== issueDetail.id,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="key">
|
||||
<div
|
||||
className="flex min-w-min items-center gap-1.5 px-4 py-2.5 pr-0"
|
||||
|
||||
@@ -36,7 +36,7 @@ export const SpreadsheetTable = observer((props: Props) => {
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<table className="divide-x-[0.5px] divide-custom-border-200 overflow-y-auto">
|
||||
<table className="overflow-y-auto">
|
||||
<SpreadsheetHeader
|
||||
displayProperties={displayProperties}
|
||||
displayFilters={displayFilters}
|
||||
|
||||
@@ -234,7 +234,6 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
issueId={issueId}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
@@ -255,7 +254,6 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
issueId={issueId}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactElement } from "react";
|
||||
import { ReactElement, useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/router";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
@@ -19,6 +19,8 @@ import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
|
||||
import { checkEmailValidity } from "helpers/string.helper";
|
||||
// type
|
||||
import { NextPageWithLayout } from "lib/types";
|
||||
// icons
|
||||
import { Eye, EyeOff } from "lucide-react";
|
||||
|
||||
type TResetPasswordFormValues = {
|
||||
email: string;
|
||||
@@ -37,6 +39,8 @@ const ResetPasswordPage: NextPageWithLayout = () => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { uidb64, token, email } = router.query;
|
||||
// states
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
// toast
|
||||
const { setToastAlert } = useToast();
|
||||
// sign in redirection hook
|
||||
@@ -117,15 +121,28 @@ const ResetPasswordPage: NextPageWithLayout = () => {
|
||||
required: "Password is required",
|
||||
}}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Input
|
||||
type="password"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
hasError={Boolean(errors.password)}
|
||||
placeholder="Enter password"
|
||||
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
||||
minLength={8}
|
||||
/>
|
||||
<div className="relative flex items-center rounded-md bg-onboarding-background-200">
|
||||
<Input
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
hasError={Boolean(errors.password)}
|
||||
placeholder="Enter password"
|
||||
className="h-[46px] w-full border border-onboarding-border-100 !bg-onboarding-background-200 pr-12 placeholder:text-onboarding-text-400"
|
||||
minLength={8}
|
||||
/>
|
||||
{showPassword ? (
|
||||
<EyeOff
|
||||
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
|
||||
onClick={() => setShowPassword(false)}
|
||||
/>
|
||||
) : (
|
||||
<Eye
|
||||
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
|
||||
onClick={() => setShowPassword(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<Button
|
||||
|
||||
@@ -268,7 +268,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
|
||||
runInAction(() => {
|
||||
update(this.issues, cycleId, (cycleIssueIds = []) => {
|
||||
uniq(concat(cycleIssueIds, issueIds));
|
||||
return uniq(concat(cycleIssueIds, issueIds));
|
||||
});
|
||||
});
|
||||
issueIds.forEach((issueId) => {
|
||||
|
||||
Reference in New Issue
Block a user