mirror of
https://github.com/makeplane/plane.git
synced 2026-07-12 21:40:18 +02:00
Merge pull request #288 from makeplane/sync/ce-ee
sync: merge conflicts need to be resolved
This commit is contained in:
0
apiserver/bin/beat → apiserver/bin/docker-entry-beat.sh
Executable file → Normal file
0
apiserver/bin/beat → apiserver/bin/docker-entry-beat.sh
Executable file → Normal file
6
apiserver/bin/docker-entry-migrator.sh
Normal file
6
apiserver/bin/docker-entry-migrator.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
python manage.py wait_for_db $1
|
||||
|
||||
python manage.py migrate $1
|
||||
@@ -1,8 +0,0 @@
|
||||
## Coolify Setup
|
||||
|
||||
Access the `coolify-docker-compose` file [here](https://raw.githubusercontent.com/makeplane/plane/master/deploy/coolify/coolify-docker-compose.yml) or download using using below command
|
||||
|
||||
```
|
||||
curl -fsSL https://raw.githubusercontent.com/makeplane/plane/master/deploy/coolify/coolify-docker-compose.yml
|
||||
|
||||
```
|
||||
@@ -1,230 +0,0 @@
|
||||
|
||||
services:
|
||||
web:
|
||||
container_name: web
|
||||
platform: linux/amd64
|
||||
image: makeplane/plane-frontend:latest
|
||||
restart: always
|
||||
command: /usr/local/bin/start.sh web/server.js web
|
||||
environment:
|
||||
- NEXT_PUBLIC_DEPLOY_URL=$SERVICE_FQDN_SPACE_8082
|
||||
depends_on:
|
||||
- api
|
||||
- worker
|
||||
|
||||
space:
|
||||
container_name: space
|
||||
platform: linux/amd64
|
||||
image: makeplane/plane-space:latest
|
||||
restart: always
|
||||
command: /usr/local/bin/start.sh space/server.js space
|
||||
environment:
|
||||
- SERVICE_FQDN_SPACE_8082=/api
|
||||
depends_on:
|
||||
- api
|
||||
- worker
|
||||
- web
|
||||
|
||||
api:
|
||||
container_name: api
|
||||
platform: linux/amd64
|
||||
image: makeplane/plane-backend:latest
|
||||
restart: always
|
||||
command: ./bin/takeoff
|
||||
environment:
|
||||
- DEBUG=${DEBUG:-0}
|
||||
- SENTRY_DSN=${SENTRY_DSN:-""}
|
||||
- PGUSER=${PGUSER:-plane}
|
||||
- PGPASSWORD=${PGPASSWORD:-plane}
|
||||
- PGHOST=${PGHOST:-plane-db}
|
||||
- PGDATABASE=${PGDATABASE:-plane}
|
||||
- DATABASE_URL=postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}/${PGDATABASE}
|
||||
- REDIS_HOST=${REDIS_HOST:-plane-redis}
|
||||
- REDIS_PORT=${REDIS_PORT:-6379}
|
||||
- REDIS_URL=redis://${REDIS_HOST}:6379/
|
||||
- EMAIL_HOST=${EMAIL_HOST:-""}
|
||||
- EMAIL_HOST_USER=${EMAIL_HOST_USER:-""}
|
||||
- EMAIL_HOST_PASSWORD=${EMAIL_HOST_PASSWORD:-""}
|
||||
- EMAIL_PORT=${EMAIL_PORT:-587}
|
||||
- EMAIL_FROM=${EMAIL_FROM:-Team Plane <team@mailer.plane.so>}
|
||||
- EMAIL_USE_TLS=${EMAIL_USE_TLS:-1}
|
||||
- EMAIL_USE_SSL=${EMAIL_USE_SSL:-0}
|
||||
- AWS_REGION=${AWS_REGION:-""}
|
||||
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-access-key}
|
||||
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-secret-key}
|
||||
- AWS_S3_ENDPOINT_URL=${AWS_S3_ENDPOINT_URL:-http://plane-minio:9000}
|
||||
- AWS_S3_BUCKET_NAME=${AWS_S3_BUCKET_NAME:-uploads}
|
||||
- FILE_SIZE_LIMIT=${FILE_SIZE_LIMIT:-5242880}
|
||||
- OPENAI_API_BASE=${OPENAI_API_BASE:-https://api.openai.com/v1}
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY:-sk-}
|
||||
- GPT_ENGINE=${GPT_ENGINE:-gpt-3.5-turbo}
|
||||
- GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET:-""}
|
||||
- DOCKERIZED=${DOCKERIZED:-1}
|
||||
- USE_MINIO=${USE_MINIO:-1}
|
||||
- NGINX_PORT=${NGINX_PORT:-8082}
|
||||
- DEFAULT_EMAIL=${DEFAULT_EMAIL:-captain@plane.so}
|
||||
- DEFAULT_PASSWORD=${DEFAULT_PASSWORD:-password123}
|
||||
- ENABLE_SIGNUP=${ENABLE_SIGNUP:-1}
|
||||
- ENABLE_EMAIL_PASSWORD=${ENABLE_EMAIL_PASSWORD:-1}
|
||||
- ENABLE_MAGIC_LINK_LOGIN=${ENABLE_MAGIC_LINK_LOGIN:-0}
|
||||
- SECRET_KEY=${SECRET_KEY:-60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5}
|
||||
- WEB_URL=$SERVICE_FQDN_PLANE_8082
|
||||
depends_on:
|
||||
- plane-db
|
||||
- plane-redis
|
||||
|
||||
worker:
|
||||
container_name: bgworker
|
||||
platform: linux/amd64
|
||||
image: makeplane/plane-backend:latest
|
||||
restart: always
|
||||
command: ./bin/worker
|
||||
environment:
|
||||
- DEBUG=${DEBUG:-0}
|
||||
- SENTRY_DSN=${SENTRY_DSN:-""}
|
||||
- PGUSER=${PGUSER:-plane}
|
||||
- PGPASSWORD=${PGPASSWORD:-plane}
|
||||
- PGHOST=${PGHOST:-plane-db}
|
||||
- PGDATABASE=${PGDATABASE:-plane}
|
||||
- DATABASE_URL=postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}/${PGDATABASE}
|
||||
- REDIS_HOST=${REDIS_HOST:-plane-redis}
|
||||
- REDIS_PORT=${REDIS_PORT:-6379}
|
||||
- REDIS_URL=redis://${REDIS_HOST}:6379/
|
||||
- EMAIL_HOST=${EMAIL_HOST:-""}
|
||||
- EMAIL_HOST_USER=${EMAIL_HOST_USER:-""}
|
||||
- EMAIL_HOST_PASSWORD=${EMAIL_HOST_PASSWORD:-""}
|
||||
- EMAIL_PORT=${EMAIL_PORT:-587}
|
||||
- EMAIL_FROM=${EMAIL_FROM:-Team Plane <team@mailer.plane.so>}
|
||||
- EMAIL_USE_TLS=${EMAIL_USE_TLS:-1}
|
||||
- EMAIL_USE_SSL=${EMAIL_USE_SSL:-0}
|
||||
- AWS_REGION=${AWS_REGION:-""}
|
||||
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-access-key}
|
||||
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-secret-key}
|
||||
- AWS_S3_ENDPOINT_URL=${AWS_S3_ENDPOINT_URL:-http://plane-minio:9000}
|
||||
- AWS_S3_BUCKET_NAME=${AWS_S3_BUCKET_NAME:-uploads}
|
||||
- FILE_SIZE_LIMIT=${FILE_SIZE_LIMIT:-5242880}
|
||||
- OPENAI_API_BASE=${OPENAI_API_BASE:-https://api.openai.com/v1}
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY:-sk-}
|
||||
- GPT_ENGINE=${GPT_ENGINE:-gpt-3.5-turbo}
|
||||
- GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET:-""}
|
||||
- DOCKERIZED=${DOCKERIZED:-1}
|
||||
- USE_MINIO=${USE_MINIO:-1}
|
||||
- NGINX_PORT=${NGINX_PORT:-8082}
|
||||
- DEFAULT_EMAIL=${DEFAULT_EMAIL:-captain@plane.so}
|
||||
- DEFAULT_PASSWORD=${DEFAULT_PASSWORD:-password123}
|
||||
- ENABLE_SIGNUP=${ENABLE_SIGNUP:-1}
|
||||
- SECRET_KEY=${SECRET_KEY:-60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5}
|
||||
depends_on:
|
||||
- api
|
||||
- plane-db
|
||||
- plane-redis
|
||||
|
||||
beat-worker:
|
||||
container_name: beatworker
|
||||
platform: linux/amd64
|
||||
image: makeplane/plane-backend:latest
|
||||
restart: always
|
||||
command: ./bin/beat
|
||||
environment:
|
||||
- DEBUG=${DEBUG:-0}
|
||||
- SENTRY_DSN=${SENTRY_DSN:-""}
|
||||
- PGUSER=${PGUSER:-plane}
|
||||
- PGPASSWORD=${PGPASSWORD:-plane}
|
||||
- PGHOST=${PGHOST:-plane-db}
|
||||
- PGDATABASE=${PGDATABASE:-plane}
|
||||
- DATABASE_URL=postgresql://${PGUSER}:${PGPASSWORD}@${PGHOST}/${PGDATABASE}
|
||||
- REDIS_HOST=${REDIS_HOST:-plane-redis}
|
||||
- REDIS_PORT=${REDIS_PORT:-6379}
|
||||
- REDIS_URL=redis://${REDIS_HOST}:6379/
|
||||
- EMAIL_HOST=${EMAIL_HOST:-""}
|
||||
- EMAIL_HOST_USER=${EMAIL_HOST_USER:-""}
|
||||
- EMAIL_HOST_PASSWORD=${EMAIL_HOST_PASSWORD:-""}
|
||||
- EMAIL_PORT=${EMAIL_PORT:-587}
|
||||
- EMAIL_FROM=${EMAIL_FROM:-Team Plane <team@mailer.plane.so>}
|
||||
- EMAIL_USE_TLS=${EMAIL_USE_TLS:-1}
|
||||
- EMAIL_USE_SSL=${EMAIL_USE_SSL:-0}
|
||||
- AWS_REGION=${AWS_REGION:-""}
|
||||
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-access-key}
|
||||
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-secret-key}
|
||||
- AWS_S3_ENDPOINT_URL=${AWS_S3_ENDPOINT_URL:-http://plane-minio:9000}
|
||||
- AWS_S3_BUCKET_NAME=${AWS_S3_BUCKET_NAME:-uploads}
|
||||
- FILE_SIZE_LIMIT=${FILE_SIZE_LIMIT:-5242880}
|
||||
- OPENAI_API_BASE=${OPENAI_API_BASE:-https://api.openai.com/v1}
|
||||
- OPENAI_API_KEY=${OPENAI_API_KEY:-sk-}
|
||||
- GPT_ENGINE=${GPT_ENGINE:-gpt-3.5-turbo}
|
||||
- GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET:-""}
|
||||
- DOCKERIZED=${DOCKERIZED:-1}
|
||||
- USE_MINIO=${USE_MINIO:-1}
|
||||
- NGINX_PORT=${NGINX_PORT:-8082}
|
||||
- DEFAULT_EMAIL=${DEFAULT_EMAIL:-captain@plane.so}
|
||||
- DEFAULT_PASSWORD=${DEFAULT_PASSWORD:-password123}
|
||||
- ENABLE_SIGNUP=${ENABLE_SIGNUP:-1}
|
||||
- SECRET_KEY=${SECRET_KEY:-60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5}
|
||||
depends_on:
|
||||
- api
|
||||
- plane-db
|
||||
- plane-redis
|
||||
|
||||
plane-db:
|
||||
container_name: plane-db
|
||||
image: postgres:15.2-alpine
|
||||
restart: always
|
||||
command: postgres -c 'max_connections=1000'
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
environment:
|
||||
- POSTGRES_USER=${POSTGRES_USER:-plane}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-plane}
|
||||
- POSTGRES_DB=${POSTGRES_DB:-plane}
|
||||
- PGDATA=${PGDATA:-/var/lib/postgresql/data}
|
||||
|
||||
plane-redis:
|
||||
container_name: plane-redis
|
||||
image: redis:7.2.4-alpine
|
||||
restart: always
|
||||
volumes:
|
||||
- redisdata:/data
|
||||
|
||||
plane-minio:
|
||||
container_name: plane-minio
|
||||
image: minio/minio
|
||||
restart: always
|
||||
command: server /export --console-address ":9090"
|
||||
volumes:
|
||||
- uploads:/export
|
||||
environment:
|
||||
- MINIO_ROOT_USER=${MINIO_ROOT_USER:-access-key}
|
||||
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-secret-key}
|
||||
|
||||
createbuckets:
|
||||
image: minio/mc
|
||||
entrypoint: >
|
||||
/bin/sh -c " /usr/bin/mc config host add plane-minio http://plane-minio:9000 \$AWS_ACCESS_KEY_ID \$AWS_SECRET_ACCESS_KEY; /usr/bin/mc mb plane-minio/\$AWS_S3_BUCKET_NAME; /usr/bin/mc anonymous set download plane-minio/\$AWS_S3_BUCKET_NAME; exit 0; "
|
||||
environment:
|
||||
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-access-key}
|
||||
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-secret-key}
|
||||
- AWS_S3_BUCKET_NAME=${AWS_S3_BUCKET_NAME:-uploads}
|
||||
depends_on:
|
||||
- plane-minio
|
||||
|
||||
# Comment this if you already have a reverse proxy running
|
||||
proxy:
|
||||
container_name: proxy
|
||||
platform: linux/amd64
|
||||
image: makeplane/plane-proxy:latest
|
||||
ports:
|
||||
- 8082:80
|
||||
environment:
|
||||
- SERVICE_FQDN_PLANE_8082
|
||||
- NGINX_PORT=${NGINX_PORT:-8082}
|
||||
- FILE_SIZE_LIMIT=${FILE_SIZE_LIMIT:-5242880}
|
||||
- BUCKET_NAME=${AWS_S3_BUCKET_NAME:-uploads}
|
||||
depends_on:
|
||||
- web
|
||||
- api
|
||||
- space
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
redisdata:
|
||||
uploads:
|
||||
@@ -86,7 +86,7 @@ services:
|
||||
platform: ${DOCKER_PLATFORM:-}
|
||||
pull_policy: ${PULL_POLICY:-always}
|
||||
restart: unless-stopped
|
||||
command: ./bin/takeoff
|
||||
command: ./bin/docker-entry-api.sh
|
||||
deploy:
|
||||
replicas: ${API_REPLICAS:-1}
|
||||
volumes:
|
||||
@@ -101,7 +101,7 @@ services:
|
||||
platform: ${DOCKER_PLATFORM:-}
|
||||
pull_policy: ${PULL_POLICY:-always}
|
||||
restart: unless-stopped
|
||||
command: ./bin/worker
|
||||
command: ./bin/docker-entry-worker.sh
|
||||
volumes:
|
||||
- logs_worker:/code/plane/logs
|
||||
depends_on:
|
||||
@@ -115,7 +115,7 @@ services:
|
||||
platform: ${DOCKER_PLATFORM:-}
|
||||
pull_policy: ${PULL_POLICY:-always}
|
||||
restart: unless-stopped
|
||||
command: ./bin/beat
|
||||
command: ./bin/docker-entry-beat.sh
|
||||
volumes:
|
||||
- logs_beat-worker:/code/plane/logs
|
||||
depends_on:
|
||||
@@ -129,9 +129,7 @@ services:
|
||||
platform: ${DOCKER_PLATFORM:-}
|
||||
pull_policy: ${PULL_POLICY:-always}
|
||||
restart: no
|
||||
command: >
|
||||
sh -c "python manage.py wait_for_db &&
|
||||
python manage.py migrate"
|
||||
command: ./bin/docker-entry-migrator.sh
|
||||
volumes:
|
||||
- logs_migrator:/code/plane/logs
|
||||
depends_on:
|
||||
|
||||
@@ -108,7 +108,7 @@ services:
|
||||
- dev_env
|
||||
volumes:
|
||||
- ./apiserver:/code
|
||||
command: ./bin/takeoff.local
|
||||
command: ./bin/docker-entry-api.sh
|
||||
env_file:
|
||||
- ./apiserver/.env
|
||||
depends_on:
|
||||
@@ -126,7 +126,7 @@ services:
|
||||
- dev_env
|
||||
volumes:
|
||||
- ./apiserver:/code
|
||||
command: ./bin/worker
|
||||
command: ./bin/docker-entry-worker.sh
|
||||
env_file:
|
||||
- ./apiserver/.env
|
||||
depends_on:
|
||||
@@ -145,7 +145,7 @@ services:
|
||||
- dev_env
|
||||
volumes:
|
||||
- ./apiserver:/code
|
||||
command: ./bin/beat
|
||||
command: ./bin/docker-entry-beat.sh
|
||||
env_file:
|
||||
- ./apiserver/.env
|
||||
depends_on:
|
||||
@@ -164,9 +164,7 @@ services:
|
||||
- dev_env
|
||||
volumes:
|
||||
- ./apiserver:/code
|
||||
command: >
|
||||
sh -c "python manage.py wait_for_db --settings=plane.settings.local &&
|
||||
python manage.py migrate --settings=plane.settings.local"
|
||||
command: ./bin/docker-entry-migrator.sh --settings=plane.settings.local
|
||||
env_file:
|
||||
- ./apiserver/.env
|
||||
depends_on:
|
||||
|
||||
@@ -45,7 +45,7 @@ services:
|
||||
args:
|
||||
DOCKER_BUILDKIT: 1
|
||||
restart: always
|
||||
command: ./bin/takeoff
|
||||
command: ./bin/docker-entry-api.sh
|
||||
env_file:
|
||||
- ./apiserver/.env
|
||||
depends_on:
|
||||
@@ -69,7 +69,7 @@ services:
|
||||
args:
|
||||
DOCKER_BUILDKIT: 1
|
||||
restart: always
|
||||
command: ./bin/worker
|
||||
command: ./bin/docker-entry-worker.sh
|
||||
env_file:
|
||||
- ./apiserver/.env
|
||||
depends_on:
|
||||
@@ -85,7 +85,7 @@ services:
|
||||
args:
|
||||
DOCKER_BUILDKIT: 1
|
||||
restart: always
|
||||
command: ./bin/beat
|
||||
command: ./bin/docker-entry-beat.sh
|
||||
env_file:
|
||||
- ./apiserver/.env
|
||||
depends_on:
|
||||
@@ -101,9 +101,7 @@ services:
|
||||
args:
|
||||
DOCKER_BUILDKIT: 1
|
||||
restart: no
|
||||
command: >
|
||||
sh -c "python manage.py wait_for_db &&
|
||||
python manage.py migrate"
|
||||
command: ./bin/docker-entry-migrator.sh
|
||||
env_file:
|
||||
- ./apiserver/.env
|
||||
depends_on:
|
||||
|
||||
@@ -17,7 +17,7 @@ export const DragHandle = forwardRef<HTMLButtonElement | null, IDragHandle>((pro
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={`mr-1 p-[2px] flex flex-shrink-0 rounded bg-custom-background-90 text-custom-sidebar-text-200 group-hover:opacity-100 cursor-grab ${
|
||||
className={` p-[2px] flex flex-shrink-0 rounded bg-custom-background-90 text-custom-sidebar-text-200 group-hover:opacity-100 cursor-grab ${
|
||||
isDragging ? "opacity-100" : "opacity-0"
|
||||
}`}
|
||||
onContextMenu={(e) => {
|
||||
|
||||
@@ -118,7 +118,7 @@ export const IssueBlockRoot: FC<Props> = observer((props) => {
|
||||
key={`${issueId}`}
|
||||
defaultHeight="3rem"
|
||||
root={containerRef}
|
||||
classNames={`relative ${isLastChild ? "" : "border-b border-b-custom-border-200"}`}
|
||||
classNames={`relative ${isLastChild && !isExpanded ? "" : "border-b border-b-custom-border-200"}`}
|
||||
>
|
||||
<IssueBlock
|
||||
issueId={issueId}
|
||||
|
||||
@@ -67,8 +67,9 @@ export const IssueBlock = observer((props: IssueBlockProps) => {
|
||||
!getIsIssuePeeked(issue.id) &&
|
||||
setPeekIssue({ workspaceSlug, projectId: issue.project_id, issueId: issue.id, nestingLevel: nestingLevel });
|
||||
|
||||
const issue = issuesMap[issueId];
|
||||
const subIssues = subIssuesStore.subIssuesByIssueId(issueId);
|
||||
const issue = issuesMap[issueId];
|
||||
const subIssuesCount = issue.sub_issues_count;
|
||||
|
||||
const { isMobile } = usePlatformOS();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -97,8 +98,6 @@ export const IssueBlock = observer((props: IssueBlockProps) => {
|
||||
|
||||
const canEditIssueProperties = canEditProperties(issue.project_id);
|
||||
const projectIdentifier = getProjectIdentifierById(issue.project_id);
|
||||
// if sub issues have been fetched for the issue, use that for count or use issue's sub_issues_count
|
||||
const subIssuesCount = subIssues ? subIssues.length : issue.sub_issues_count;
|
||||
|
||||
const paddingLeft = `${spacingLeft}px`;
|
||||
|
||||
@@ -120,7 +119,7 @@ export const IssueBlock = observer((props: IssueBlockProps) => {
|
||||
<div
|
||||
ref={issueRef}
|
||||
className={cn(
|
||||
"group min-h-11 relative flex flex-col md:flex-row md:items-center gap-3 bg-custom-background-100 p-3 text-sm",
|
||||
"group min-h-11 relative flex flex-col md:flex-row md:items-center gap-3 bg-custom-background-100 pl-1.5 pr-1 text-sm",
|
||||
{
|
||||
"border border-custom-primary-70 hover:border-custom-primary-70":
|
||||
getIsIssuePeeked(issue.id) && peekIssue?.nestingLevel === nestingLevel,
|
||||
@@ -146,7 +145,7 @@ export const IssueBlock = observer((props: IssueBlockProps) => {
|
||||
</div>
|
||||
</div>
|
||||
{displayProperties && displayProperties?.key && (
|
||||
<div className="flex-shrink-0 text-xs font-medium text-custom-text-300">
|
||||
<div className="pl-1 flex-shrink-0 text-xs font-medium text-custom-text-300">
|
||||
{projectIdentifier}-{issue.sequence_id}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -65,7 +65,7 @@ export const HeaderGroupByCard = observer(
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative flex w-full flex-shrink-0 flex-row items-center gap-2 py-1.5">
|
||||
<div className="relative flex w-full flex-shrink-0 flex-row items-center gap-1.5 py-1.5">
|
||||
<div className="flex h-5 w-5 flex-shrink-0 items-center justify-center overflow-hidden rounded-sm">
|
||||
{icon ? icon : <CircleDashed className="h-3.5 w-3.5" strokeWidth={2} />}
|
||||
</div>
|
||||
|
||||
@@ -152,7 +152,7 @@ export const ListQuickAddIssueForm: FC<IListQuickAddIssueForm> = observer((props
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className="flex w-full cursor-pointer items-center gap-2 p-3 py-3 text-custom-primary-100"
|
||||
className="flex w-full cursor-pointer items-center gap-3 p-6 py-3 text-custom-primary-100"
|
||||
onClick={() => setIsOpen(true)}
|
||||
>
|
||||
<PlusIcon className="h-3.5 w-3.5 stroke-2" />
|
||||
|
||||
@@ -2,10 +2,13 @@ import { SyntheticEvent, useCallback, useMemo } from "react";
|
||||
import xor from "lodash/xor";
|
||||
import { observer } from "mobx-react";
|
||||
import { useRouter } from "next/router";
|
||||
// icons
|
||||
import { CalendarCheck2, CalendarClock, Layers, Link, Paperclip } from "lucide-react";
|
||||
// types
|
||||
import { TIssue, IIssueDisplayProperties, TIssuePriorities } from "@plane/types";
|
||||
// hooks
|
||||
// ui
|
||||
import { Tooltip } from "@plane/ui";
|
||||
// components
|
||||
import {
|
||||
DateDropdown,
|
||||
EstimateDropdown,
|
||||
@@ -15,23 +18,19 @@ import {
|
||||
CycleDropdown,
|
||||
StateDropdown,
|
||||
} from "@/components/dropdowns";
|
||||
// helpers
|
||||
|
||||
// types
|
||||
// constants
|
||||
import { ISSUE_UPDATED } from "@/constants/event-tracker";
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||
import { shouldHighlightIssueDueDate } from "@/helpers/issue.helper";
|
||||
// hooks
|
||||
import { useEventTracker, useEstimate, useLabel, useIssues, useProjectState, useProject } from "@/hooks/store";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// components
|
||||
// local components
|
||||
import { IssuePropertyLabels } from "../properties/labels";
|
||||
import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC";
|
||||
// helpers
|
||||
// types
|
||||
// constants
|
||||
|
||||
export interface IIssueProperties {
|
||||
issue: TIssue;
|
||||
@@ -64,6 +63,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
||||
const currentLayout = `${activeLayout} layout`;
|
||||
// derived values
|
||||
const stateDetails = getStateById(issue.state_id);
|
||||
const subIssueCount = issue.sub_issues_count;
|
||||
|
||||
const issueOperations = useMemo(
|
||||
() => ({
|
||||
@@ -414,24 +414,25 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
||||
<WithDisplayPropertiesHOC
|
||||
displayProperties={displayProperties}
|
||||
displayPropertyKey="sub_issue_count"
|
||||
shouldRenderProperty={(properties) => !!properties.sub_issue_count && !!issue.sub_issues_count}
|
||||
shouldRenderProperty={(properties) => !!properties.sub_issue_count && !!subIssueCount}
|
||||
>
|
||||
<Tooltip tooltipHeading="Sub-issues" tooltipContent={`${issue.sub_issues_count}`} isMobile={isMobile}>
|
||||
<Tooltip tooltipHeading="Sub-issues" tooltipContent={`${subIssueCount}`} isMobile={isMobile}>
|
||||
<div
|
||||
onFocus={handleEventPropagation}
|
||||
onClick={(e) => {
|
||||
handleEventPropagation(e);
|
||||
if (issue.sub_issues_count) redirectToIssueDetail();
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (subIssueCount) redirectToIssueDetail();
|
||||
}}
|
||||
className={cn(
|
||||
"flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded border-[0.5px] border-custom-border-300 px-2.5 py-1",
|
||||
{
|
||||
"hover:bg-custom-background-80 cursor-pointer": issue.sub_issues_count,
|
||||
"hover:bg-custom-background-80 cursor-pointer": subIssueCount,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<Layers className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
|
||||
<div className="text-xs">{issue.sub_issues_count}</div>
|
||||
<div className="text-xs">{subIssueCount}</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</WithDisplayPropertiesHOC>
|
||||
|
||||
@@ -18,6 +18,8 @@ export const SpreadsheetSubIssueColumn: React.FC<Props> = observer((props: Props
|
||||
const router = useRouter();
|
||||
// hooks
|
||||
const { workspaceSlug } = useAppRouter();
|
||||
// derived values
|
||||
const subIssueCount = issue.sub_issues_count;
|
||||
|
||||
const redirectToIssueDetail = () => {
|
||||
router.push({
|
||||
@@ -30,15 +32,15 @@ export const SpreadsheetSubIssueColumn: React.FC<Props> = observer((props: Props
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={issue?.sub_issues_count ? redirectToIssueDetail : () => {}}
|
||||
onClick={subIssueCount ? redirectToIssueDetail : () => {}}
|
||||
className={cn(
|
||||
"flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 px-2.5 py-1 text-xs hover:bg-custom-background-80",
|
||||
{
|
||||
"cursor-pointer": issue?.sub_issues_count,
|
||||
"cursor-pointer": subIssueCount,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{issue?.sub_issues_count} {issue?.sub_issues_count === 1 ? "sub-issue" : "sub-issues"}
|
||||
{subIssueCount} {subIssueCount === 1 ? "sub-issue" : "sub-issues"}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -203,6 +203,7 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
||||
};
|
||||
|
||||
const disableUserActions = !canEditProperties(issueDetail.project_id);
|
||||
const subIssuesCount = issueDetail.sub_issues_count;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -230,7 +231,7 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
||||
{/* bulk ops */}
|
||||
<span className="size-3.5" />
|
||||
<div className="flex size-4 items-center justify-center">
|
||||
{issueDetail.sub_issues_count > 0 && (
|
||||
{subIssuesCount > 0 && (
|
||||
<button
|
||||
className="flex items-center justify-center size-4 cursor-pointer rounded-sm text-custom-text-400 hover:text-custom-text-300"
|
||||
onClick={handleToggleExpand}
|
||||
|
||||
@@ -59,6 +59,7 @@ export const IssueListItem: React.FC<ISubIssues> = observer((props) => {
|
||||
undefined;
|
||||
|
||||
const subIssueHelpers = subIssueHelpersByIssueId(parentIssueId);
|
||||
const subIssueCount = issue?.sub_issues_count || 0;
|
||||
|
||||
const handleIssuePeekOverview = (issue: TIssue) =>
|
||||
workspaceSlug &&
|
||||
@@ -77,7 +78,7 @@ export const IssueListItem: React.FC<ISubIssues> = observer((props) => {
|
||||
style={{ paddingLeft: `${spacingLeft}px` }}
|
||||
>
|
||||
<div className="h-[22px] w-[22px] flex-shrink-0">
|
||||
{issue?.sub_issues_count > 0 && (
|
||||
{subIssueCount > 0 && (
|
||||
<>
|
||||
{subIssueHelpers.preview_loader.includes(issue.id) ? (
|
||||
<div className="flex h-full w-full cursor-not-allowed items-center justify-center rounded-sm bg-custom-background-80 transition-all">
|
||||
@@ -205,7 +206,7 @@ export const IssueListItem: React.FC<ISubIssues> = observer((props) => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{subIssueHelpers.issue_visibility.includes(issueId) && issue.sub_issues_count && issue.sub_issues_count > 0 && (
|
||||
{subIssueHelpers.issue_visibility.includes(issueId) && subIssueCount > 0 && (
|
||||
<IssueList
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={issue.project_id}
|
||||
|
||||
@@ -174,6 +174,13 @@ export class IssueSubIssuesStore implements IIssueSubIssuesStore {
|
||||
|
||||
this.rootIssueDetailStore.rootIssueStore.issues.addIssue(subIssues);
|
||||
|
||||
// update sub-issues_count of the parent issue
|
||||
set(
|
||||
this.rootIssueDetailStore.rootIssueStore.issues.issuesMap,
|
||||
[parentIssueId, "sub_issues_count"],
|
||||
this.subIssues[parentIssueId].length
|
||||
);
|
||||
|
||||
return;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
@@ -270,6 +277,12 @@ export class IssueSubIssuesStore implements IIssueSubIssuesStore {
|
||||
|
||||
runInAction(() => {
|
||||
pull(this.subIssues[parentIssueId], issueId);
|
||||
// update sub-issues_count of the parent issue
|
||||
set(
|
||||
this.rootIssueDetailStore.rootIssueStore.issues.issuesMap,
|
||||
[parentIssueId, "sub_issues_count"],
|
||||
this.subIssues[parentIssueId].length
|
||||
);
|
||||
});
|
||||
|
||||
return;
|
||||
@@ -301,6 +314,12 @@ export class IssueSubIssuesStore implements IIssueSubIssuesStore {
|
||||
|
||||
runInAction(() => {
|
||||
pull(this.subIssues[parentIssueId], issueId);
|
||||
// update sub-issues_count of the parent issue
|
||||
set(
|
||||
this.rootIssueDetailStore.rootIssueStore.issues.issuesMap,
|
||||
[parentIssueId, "sub_issues_count"],
|
||||
this.subIssues[parentIssueId].length
|
||||
);
|
||||
});
|
||||
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user