Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 397cecc3a1 |
@@ -290,6 +290,5 @@ jobs:
|
||||
${{ github.workspace }}/deploy/selfhost/setup.sh
|
||||
${{ github.workspace }}/deploy/selfhost/swarm.sh
|
||||
${{ github.workspace }}/deploy/selfhost/restore.sh
|
||||
${{ github.workspace }}/deploy/selfhost/restore-airgapped.sh
|
||||
${{ github.workspace }}/deploy/selfhost/docker-compose.yml
|
||||
${{ github.workspace }}/deploy/selfhost/variables.env
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
name: Test Pull Request
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
types: ["opened", "synchronize", "ready_for_review"]
|
||||
paths:
|
||||
- 'apiserver/**'
|
||||
- '.github/workflows/test-pull-request.yml'
|
||||
|
||||
jobs:
|
||||
test-apiserver:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:14
|
||||
env:
|
||||
POSTGRES_PASSWORD: plane
|
||||
POSTGRES_USER: plane
|
||||
POSTGRES_DB: plane
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 6379:6379
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Cache Python dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements/test.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
cd apiserver
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements/test.txt
|
||||
|
||||
- name: Set up test environment
|
||||
run: |
|
||||
cd apiserver
|
||||
cat > .env << EOF
|
||||
# Basic Django settings
|
||||
DEBUG=1
|
||||
SECRET_KEY=test-secret-key-for-ci-only-do-not-use-in-production
|
||||
|
||||
# Database Configuration
|
||||
DATABASE_URL=postgres://plane:plane@localhost:5432/plane
|
||||
|
||||
# Redis Configuration
|
||||
REDIS_URL=redis://localhost:6379
|
||||
|
||||
# Email Backend for Testing
|
||||
EMAIL_BACKEND=django.core.mail.backends.locmem.EmailBackend
|
||||
|
||||
# CORS Settings for Testing
|
||||
CORS_ALLOW_ALL_ORIGINS=True
|
||||
CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3001,http://localhost:3002
|
||||
|
||||
# Disable SSL and security features for testing
|
||||
SECURE_SSL_REDIRECT=False
|
||||
SECURE_HSTS_SECONDS=0
|
||||
SESSION_COOKIE_SECURE=False
|
||||
CSRF_COOKIE_SECURE=False
|
||||
|
||||
# Instance settings
|
||||
INSTANCE_KEY=test-instance-key-for-ci
|
||||
SKIP_ENV_VAR=1
|
||||
|
||||
# File upload settings for testing
|
||||
FILE_SIZE_LIMIT=5242880
|
||||
USE_MINIO=0
|
||||
|
||||
# Base URLs for testing
|
||||
WEB_URL=http://localhost:8000
|
||||
APP_BASE_URL=http://localhost:3000
|
||||
ADMIN_BASE_URL=http://localhost:3001
|
||||
SPACE_BASE_URL=http://localhost:3002
|
||||
LIVE_BASE_URL=http://localhost:3100
|
||||
|
||||
# Session settings
|
||||
SESSION_COOKIE_AGE=604800
|
||||
SESSION_COOKIE_NAME=session-id
|
||||
ADMIN_SESSION_COOKIE_AGE=3600
|
||||
|
||||
# API settings
|
||||
API_KEY_RATE_LIMIT=60/minute
|
||||
|
||||
# Disable external services for testing
|
||||
ENABLE_SIGNUP=1
|
||||
POSTHOG_API_KEY=
|
||||
ANALYTICS_SECRET_KEY=
|
||||
GITHUB_ACCESS_TOKEN=
|
||||
UNSPLASH_ACCESS_KEY=
|
||||
|
||||
# RabbitMQ/Celery settings (will be mocked in tests)
|
||||
RABBITMQ_HOST=localhost
|
||||
RABBITMQ_PORT=5672
|
||||
RABBITMQ_USER=guest
|
||||
RABBITMQ_PASSWORD=guest
|
||||
RABBITMQ_VHOST=/
|
||||
|
||||
# AWS/Storage settings (will be mocked in tests)
|
||||
AWS_ACCESS_KEY_ID=test-access-key
|
||||
AWS_SECRET_ACCESS_KEY=test-secret-key
|
||||
AWS_S3_BUCKET_NAME=test-uploads
|
||||
AWS_REGION=us-east-1
|
||||
EOF
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
cd apiserver
|
||||
python run_tests.py -u -v --coverage
|
||||
|
||||
- name: Run contract tests
|
||||
run: |
|
||||
cd apiserver
|
||||
python run_tests.py -c -v
|
||||
|
||||
- name: Show coverage summary
|
||||
if: always()
|
||||
run: |
|
||||
cd apiserver
|
||||
python -m coverage report --show-missing
|
||||
|
||||
- name: Upload coverage reports
|
||||
uses: codecov/codecov-action@v4
|
||||
if: always() && env.CODECOV_TOKEN != ''
|
||||
with:
|
||||
file: ./apiserver/coverage.xml
|
||||
flags: apiserver
|
||||
name: apiserver-coverage
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
- name: Generate coverage badge
|
||||
if: always()
|
||||
run: |
|
||||
cd apiserver
|
||||
coverage-badge -o coverage.svg
|
||||
continue-on-error: true
|
||||
|
||||
test-summary:
|
||||
if: always() && github.event.pull_request.draft == false
|
||||
needs: [test-apiserver]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Test Results Summary
|
||||
run: |
|
||||
echo "# Test Results Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [[ "${{ needs.test-apiserver.result }}" == "success" ]]; then
|
||||
echo "✅ **API Server Tests**: PASSED" >> $GITHUB_STEP_SUMMARY
|
||||
echo "All tests completed successfully with coverage reporting." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "❌ **API Server Tests**: FAILED" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Tests failed. Please check the logs for details." >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "## Test Categories Executed" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Unit Tests**: Fast, isolated component tests" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- **Contract Tests**: API endpoint verification" >> $GITHUB_STEP_SUMMARY
|
||||
+3
-3
@@ -69,14 +69,14 @@ chmod +x setup.sh
|
||||
docker compose -f docker-compose-local.yml up
|
||||
```
|
||||
|
||||
4. Start web apps:
|
||||
5. Start web apps:
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
||||
|
||||
5. Open your browser to http://localhost:3001/god-mode/ and register yourself as instance admin
|
||||
6. Open up your browser to http://localhost:3000 then log in using the same credentials from the previous step
|
||||
6. Open your browser to http://localhost:3001/god-mode/ and register yourself as instance admin
|
||||
7. Open up your browser to http://localhost:3000 then log in using the same credentials from the previous step
|
||||
|
||||
That’s it! You’re all set to begin coding. Remember to refresh your browser if changes don’t auto-reload. Happy contributing! 🎉
|
||||
|
||||
|
||||
@@ -10,13 +10,11 @@ type Props = {
|
||||
handleClose: () => void;
|
||||
};
|
||||
|
||||
const ESendEmailSteps = {
|
||||
SEND_EMAIL: "SEND_EMAIL",
|
||||
SUCCESS: "SUCCESS",
|
||||
FAILED: "FAILED",
|
||||
} as const;
|
||||
|
||||
type ESendEmailSteps = typeof ESendEmailSteps[keyof typeof ESendEmailSteps];
|
||||
enum ESendEmailSteps {
|
||||
SEND_EMAIL = "SEND_EMAIL",
|
||||
SUCCESS = "SUCCESS",
|
||||
FAILED = "FAILED",
|
||||
}
|
||||
|
||||
const instanceService = new InstanceService();
|
||||
|
||||
|
||||
@@ -16,16 +16,14 @@ import { Banner, PasswordStrengthMeter } from "@/components/common";
|
||||
const authService = new AuthService();
|
||||
|
||||
// error codes
|
||||
const EErrorCodes = {
|
||||
INSTANCE_NOT_CONFIGURED: "INSTANCE_NOT_CONFIGURED",
|
||||
ADMIN_ALREADY_EXIST: "ADMIN_ALREADY_EXIST",
|
||||
REQUIRED_EMAIL_PASSWORD_FIRST_NAME: "REQUIRED_EMAIL_PASSWORD_FIRST_NAME",
|
||||
INVALID_EMAIL: "INVALID_EMAIL",
|
||||
INVALID_PASSWORD: "INVALID_PASSWORD",
|
||||
USER_ALREADY_EXISTS: "USER_ALREADY_EXISTS",
|
||||
} as const;
|
||||
|
||||
type EErrorCodes = typeof EErrorCodes[keyof typeof EErrorCodes];
|
||||
enum EErrorCodes {
|
||||
INSTANCE_NOT_CONFIGURED = "INSTANCE_NOT_CONFIGURED",
|
||||
ADMIN_ALREADY_EXIST = "ADMIN_ALREADY_EXIST",
|
||||
REQUIRED_EMAIL_PASSWORD_FIRST_NAME = "REQUIRED_EMAIL_PASSWORD_FIRST_NAME",
|
||||
INVALID_EMAIL = "INVALID_EMAIL",
|
||||
INVALID_PASSWORD = "INVALID_PASSWORD",
|
||||
USER_ALREADY_EXISTS = "USER_ALREADY_EXISTS",
|
||||
}
|
||||
|
||||
type TError = {
|
||||
type: EErrorCodes | undefined;
|
||||
@@ -146,7 +144,7 @@ export const InstanceSetupForm: FC = (props) => {
|
||||
|
||||
{errorData.type &&
|
||||
errorData?.message &&
|
||||
!([EErrorCodes.INVALID_EMAIL, EErrorCodes.INVALID_PASSWORD] as EErrorCodes[]).includes(errorData.type) && (
|
||||
![EErrorCodes.INVALID_EMAIL, EErrorCodes.INVALID_PASSWORD].includes(errorData.type) && (
|
||||
<Banner type="error" message={errorData?.message} />
|
||||
)}
|
||||
|
||||
|
||||
@@ -18,15 +18,13 @@ import { AuthBanner } from "../authentication";
|
||||
const authService = new AuthService();
|
||||
|
||||
// error codes
|
||||
const EErrorCodes = {
|
||||
INSTANCE_NOT_CONFIGURED: "INSTANCE_NOT_CONFIGURED",
|
||||
REQUIRED_EMAIL_PASSWORD: "REQUIRED_EMAIL_PASSWORD",
|
||||
INVALID_EMAIL: "INVALID_EMAIL",
|
||||
USER_DOES_NOT_EXIST: "USER_DOES_NOT_EXIST",
|
||||
AUTHENTICATION_FAILED: "AUTHENTICATION_FAILED",
|
||||
} as const;
|
||||
|
||||
type EErrorCodes = typeof EErrorCodes[keyof typeof EErrorCodes];
|
||||
enum EErrorCodes {
|
||||
INSTANCE_NOT_CONFIGURED = "INSTANCE_NOT_CONFIGURED",
|
||||
REQUIRED_EMAIL_PASSWORD = "REQUIRED_EMAIL_PASSWORD",
|
||||
INVALID_EMAIL = "INVALID_EMAIL",
|
||||
USER_DOES_NOT_EXIST = "USER_DOES_NOT_EXIST",
|
||||
AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED",
|
||||
}
|
||||
|
||||
type TError = {
|
||||
type: EErrorCodes | undefined;
|
||||
|
||||
@@ -20,15 +20,13 @@ import githubDarkModeImage from "@/public/logos/github-white.png";
|
||||
import GitlabLogo from "@/public/logos/gitlab-logo.svg";
|
||||
import GoogleLogo from "@/public/logos/google-logo.svg";
|
||||
|
||||
export const EErrorAlertType = {
|
||||
BANNER_ALERT: "BANNER_ALERT",
|
||||
INLINE_FIRST_NAME: "INLINE_FIRST_NAME",
|
||||
INLINE_EMAIL: "INLINE_EMAIL",
|
||||
INLINE_PASSWORD: "INLINE_PASSWORD",
|
||||
INLINE_EMAIL_CODE: "INLINE_EMAIL_CODE",
|
||||
} as const;
|
||||
|
||||
export type EErrorAlertType = typeof EErrorAlertType[keyof typeof EErrorAlertType];
|
||||
export enum EErrorAlertType {
|
||||
BANNER_ALERT = "BANNER_ALERT",
|
||||
INLINE_FIRST_NAME = "INLINE_FIRST_NAME",
|
||||
INLINE_EMAIL = "INLINE_EMAIL",
|
||||
INLINE_PASSWORD = "INLINE_PASSWORD",
|
||||
INLINE_EMAIL_CODE = "INLINE_EMAIL_CODE",
|
||||
}
|
||||
|
||||
const errorCodeMessages: {
|
||||
[key in EAdminAuthErrorCodes]: { title: string; message: (email?: string | undefined) => ReactNode };
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "admin",
|
||||
"description": "Admin UI for Plane",
|
||||
"version": "0.26.1",
|
||||
"version": "0.26.0",
|
||||
"license": "AGPL-3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -50,6 +50,6 @@
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@types/zxcvbn": "^4.4.4",
|
||||
"typescript": "5.8.3"
|
||||
"typescript": "5.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "plane-api",
|
||||
"version": "0.26.1",
|
||||
"version": "0.26.0",
|
||||
"license": "AGPL-3.0",
|
||||
"private": true,
|
||||
"description": "API server powering Plane's backend"
|
||||
|
||||
@@ -58,7 +58,7 @@ from plane.bgtasks.storage_metadata_task import get_asset_object_metadata
|
||||
from .base import BaseAPIView
|
||||
from plane.utils.host import base_host
|
||||
from plane.bgtasks.webhook_task import model_activity
|
||||
from plane.bgtasks.work_item_link_task import crawl_work_item_link_title
|
||||
|
||||
|
||||
class WorkspaceIssueAPIEndpoint(BaseAPIView):
|
||||
"""
|
||||
@@ -692,9 +692,6 @@ class IssueLinkAPIEndpoint(BaseAPIView):
|
||||
serializer = IssueLinkSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
serializer.save(project_id=project_id, issue_id=issue_id)
|
||||
crawl_work_item_link_title.delay(
|
||||
serializer.data.get("id"), serializer.data.get("url")
|
||||
)
|
||||
|
||||
link = IssueLink.objects.get(pk=serializer.data["id"])
|
||||
link.created_by_id = request.data.get("created_by", request.user.id)
|
||||
@@ -722,9 +719,6 @@ class IssueLinkAPIEndpoint(BaseAPIView):
|
||||
serializer = IssueLinkSerializer(issue_link, data=request.data, partial=True)
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
crawl_work_item_link_title.delay(
|
||||
serializer.data.get("id"), serializer.data.get("url")
|
||||
)
|
||||
issue_activity.delay(
|
||||
type="link.activity.updated",
|
||||
requested_data=requested_data,
|
||||
|
||||
@@ -45,7 +45,7 @@ class IssueLinkViewSet(BaseViewSet):
|
||||
serializer = IssueLinkSerializer(data=request.data)
|
||||
if serializer.is_valid():
|
||||
serializer.save(project_id=project_id, issue_id=issue_id)
|
||||
crawl_work_item_link_title.delay(
|
||||
crawl_work_item_link_title(
|
||||
serializer.data.get("id"), serializer.data.get("url")
|
||||
)
|
||||
issue_activity.delay(
|
||||
@@ -78,7 +78,7 @@ class IssueLinkViewSet(BaseViewSet):
|
||||
serializer = IssueLinkSerializer(issue_link, data=request.data, partial=True)
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
crawl_work_item_link_title.delay(
|
||||
crawl_work_item_link_title(
|
||||
serializer.data.get("id"), serializer.data.get("url")
|
||||
)
|
||||
|
||||
|
||||
@@ -168,8 +168,6 @@ class ProjectMemberViewSet(BaseViewSet):
|
||||
workspace__slug=slug,
|
||||
member__is_bot=False,
|
||||
is_active=True,
|
||||
member__member_workspace__workspace__slug=slug,
|
||||
member__member_workspace__is_active=True,
|
||||
).select_related("project", "member", "workspace")
|
||||
|
||||
serializer = ProjectMemberRoleSerializer(
|
||||
@@ -315,11 +313,7 @@ class UserProjectRolesEndpoint(BaseAPIView):
|
||||
|
||||
def get(self, request, slug):
|
||||
project_members = ProjectMember.objects.filter(
|
||||
workspace__slug=slug,
|
||||
member_id=request.user.id,
|
||||
is_active=True,
|
||||
member__member_workspace__workspace__slug=slug,
|
||||
member__member_workspace__is_active=True,
|
||||
workspace__slug=slug, member_id=request.user.id, is_active=True
|
||||
).values("project_id", "role")
|
||||
|
||||
project_members = {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
# Django imports
|
||||
from django.db.models import Count, Q, OuterRef, Subquery, IntegerField
|
||||
from django.utils import timezone
|
||||
from django.db.models.functions import Coalesce
|
||||
|
||||
# Third party modules
|
||||
@@ -134,7 +133,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
||||
# Deactivate the users from the projects where the user is part of
|
||||
_ = ProjectMember.objects.filter(
|
||||
workspace__slug=slug, member_id=workspace_member.member_id, is_active=True
|
||||
).update(is_active=False, updated_at=timezone.now())
|
||||
).update(is_active=False)
|
||||
|
||||
workspace_member.is_active = False
|
||||
workspace_member.save()
|
||||
@@ -195,7 +194,7 @@ class WorkSpaceMemberViewSet(BaseViewSet):
|
||||
# # Deactivate the users from the projects where the user is part of
|
||||
_ = ProjectMember.objects.filter(
|
||||
workspace__slug=slug, member_id=workspace_member.member_id, is_active=True
|
||||
).update(is_active=False, updated_at=timezone.now())
|
||||
).update(is_active=False)
|
||||
|
||||
# # Deactivate the user
|
||||
workspace_member.is_active = False
|
||||
|
||||
@@ -284,7 +284,6 @@ def send_email_notification(
|
||||
"project": str(issue.project.name),
|
||||
"user_preference": f"{base_api}/profile/preferences/email",
|
||||
"comments": comments,
|
||||
"entity_type": "issue",
|
||||
}
|
||||
html_content = render_to_string(
|
||||
"emails/notifications/issue-updates.html", context
|
||||
|
||||
@@ -19,6 +19,17 @@ logger = logging.getLogger("plane.worker")
|
||||
|
||||
DEFAULT_FAVICON = "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWxpbmstaWNvbiBsdWNpZGUtbGluayI+PHBhdGggZD0iTTEwIDEzYTUgNSAwIDAgMCA3LjU0LjU0bDMtM2E1IDUgMCAwIDAtNy4wNy03LjA3bC0xLjcyIDEuNzEiLz48cGF0aCBkPSJNMTQgMTFhNSA1IDAgMCAwLTcuNTQtLjU0bC0zIDNhNSA1IDAgMCAwIDcuMDcgNy4wN2wxLjcxLTEuNzEiLz48L3N2Zz4=" # noqa: E501
|
||||
|
||||
|
||||
@shared_task
|
||||
def crawl_work_item_link_title(id: str, url: str) -> None:
|
||||
meta_data = crawl_work_item_link_title_and_favicon(url)
|
||||
issue_link = IssueLink.objects.get(id=id)
|
||||
|
||||
issue_link.metadata = meta_data
|
||||
|
||||
issue_link.save()
|
||||
|
||||
|
||||
def crawl_work_item_link_title_and_favicon(url: str) -> Dict[str, Any]:
|
||||
"""
|
||||
Crawls a URL to extract the title and favicon.
|
||||
@@ -46,18 +57,17 @@ def crawl_work_item_link_title_and_favicon(url: str) -> Dict[str, Any]:
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" # noqa: E501
|
||||
}
|
||||
|
||||
soup = None
|
||||
title = None
|
||||
# Fetch the main page
|
||||
response = requests.get(url, headers=headers, timeout=2)
|
||||
|
||||
try:
|
||||
response = requests.get(url, headers=headers, timeout=1)
|
||||
response.raise_for_status()
|
||||
|
||||
soup = BeautifulSoup(response.content, "html.parser")
|
||||
title_tag = soup.find("title")
|
||||
title = title_tag.get_text().strip() if title_tag else None
|
||||
# Parse HTML
|
||||
soup = BeautifulSoup(response.content, "html.parser")
|
||||
|
||||
except requests.RequestException as e:
|
||||
logger.warning(f"Failed to fetch HTML for title: {str(e)}")
|
||||
# Extract title
|
||||
title_tag = soup.find("title")
|
||||
title = title_tag.get_text().strip() if title_tag else None
|
||||
|
||||
# Fetch and encode favicon
|
||||
favicon_base64 = fetch_and_encode_favicon(headers, soup, url)
|
||||
@@ -72,6 +82,14 @@ def crawl_work_item_link_title_and_favicon(url: str) -> Dict[str, Any]:
|
||||
|
||||
return result
|
||||
|
||||
except requests.RequestException as e:
|
||||
log_exception(e)
|
||||
return {
|
||||
"error": f"Request failed: {str(e)}",
|
||||
"title": None,
|
||||
"favicon": None,
|
||||
"url": url,
|
||||
}
|
||||
except Exception as e:
|
||||
log_exception(e)
|
||||
return {
|
||||
@@ -82,7 +100,7 @@ def crawl_work_item_link_title_and_favicon(url: str) -> Dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def find_favicon_url(soup: Optional[BeautifulSoup], base_url: str) -> Optional[str]:
|
||||
def find_favicon_url(soup: BeautifulSoup, base_url: str) -> Optional[str]:
|
||||
"""
|
||||
Find the favicon URL from HTML soup.
|
||||
|
||||
@@ -93,20 +111,18 @@ def find_favicon_url(soup: Optional[BeautifulSoup], base_url: str) -> Optional[s
|
||||
Returns:
|
||||
str: Absolute URL to favicon or None
|
||||
"""
|
||||
# Look for various favicon link tags
|
||||
favicon_selectors = [
|
||||
'link[rel="icon"]',
|
||||
'link[rel="shortcut icon"]',
|
||||
'link[rel="apple-touch-icon"]',
|
||||
'link[rel="apple-touch-icon-precomposed"]',
|
||||
]
|
||||
|
||||
if soup is not None:
|
||||
# Look for various favicon link tags
|
||||
favicon_selectors = [
|
||||
'link[rel="icon"]',
|
||||
'link[rel="shortcut icon"]',
|
||||
'link[rel="apple-touch-icon"]',
|
||||
'link[rel="apple-touch-icon-precomposed"]',
|
||||
]
|
||||
|
||||
for selector in favicon_selectors:
|
||||
favicon_tag = soup.select_one(selector)
|
||||
if favicon_tag and favicon_tag.get("href"):
|
||||
return urljoin(base_url, favicon_tag["href"])
|
||||
for selector in favicon_selectors:
|
||||
favicon_tag = soup.select_one(selector)
|
||||
if favicon_tag and favicon_tag.get("href"):
|
||||
return urljoin(base_url, favicon_tag["href"])
|
||||
|
||||
# Fallback to /favicon.ico
|
||||
parsed_url = urlparse(base_url)
|
||||
@@ -115,6 +131,7 @@ def find_favicon_url(soup: Optional[BeautifulSoup], base_url: str) -> Optional[s
|
||||
# Check if fallback exists
|
||||
try:
|
||||
response = requests.head(fallback_url, timeout=2)
|
||||
response.raise_for_status()
|
||||
if response.status_code == 200:
|
||||
return fallback_url
|
||||
except requests.RequestException as e:
|
||||
@@ -125,8 +142,8 @@ def find_favicon_url(soup: Optional[BeautifulSoup], base_url: str) -> Optional[s
|
||||
|
||||
|
||||
def fetch_and_encode_favicon(
|
||||
headers: Dict[str, str], soup: Optional[BeautifulSoup], url: str
|
||||
) -> Dict[str, Optional[str]]:
|
||||
headers: Dict[str, str], soup: BeautifulSoup, url: str
|
||||
) -> Optional[Dict[str, str]]:
|
||||
"""
|
||||
Fetch favicon and encode it as base64.
|
||||
|
||||
@@ -145,7 +162,8 @@ def fetch_and_encode_favicon(
|
||||
"favicon_base64": f"data:image/svg+xml;base64,{DEFAULT_FAVICON}",
|
||||
}
|
||||
|
||||
response = requests.get(favicon_url, headers=headers, timeout=1)
|
||||
response = requests.get(favicon_url, headers=headers, timeout=2)
|
||||
response.raise_for_status()
|
||||
|
||||
# Get content type
|
||||
content_type = response.headers.get("content-type", "image/x-icon")
|
||||
@@ -165,13 +183,3 @@ def fetch_and_encode_favicon(
|
||||
"favicon_url": None,
|
||||
"favicon_base64": f"data:image/svg+xml;base64,{DEFAULT_FAVICON}",
|
||||
}
|
||||
|
||||
|
||||
@shared_task
|
||||
def crawl_work_item_link_title(id: str, url: str) -> None:
|
||||
meta_data = crawl_work_item_link_title_and_favicon(url)
|
||||
issue_link = IssueLink.objects.get(id=id)
|
||||
|
||||
issue_link.metadata = meta_data
|
||||
|
||||
issue_link.save()
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# Generated by Django 4.2.21 on 2025-06-06 12:33
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('db', '0096_user_is_email_valid_user_masked_at'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='project',
|
||||
name='external_id',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='project',
|
||||
name='external_source',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
]
|
||||
@@ -122,9 +122,6 @@ class Project(BaseModel):
|
||||
# timezone
|
||||
TIMEZONE_CHOICES = tuple(zip(pytz.all_timezones, pytz.all_timezones))
|
||||
timezone = models.CharField(max_length=255, default="UTC", choices=TIMEZONE_CHOICES)
|
||||
# external_id for imports
|
||||
external_source = models.CharField(max_length=255, null=True, blank=True)
|
||||
external_id = models.CharField(max_length=255, blank=True, null=True)
|
||||
|
||||
@property
|
||||
def cover_image_url(self):
|
||||
|
||||
@@ -12,6 +12,29 @@ Tests are organized into the following categories:
|
||||
- **App tests**: Test the web application API endpoints (under `/api/`).
|
||||
- **Smoke tests**: Basic tests to verify that the application runs correctly.
|
||||
|
||||
## Continuous Integration (CI)
|
||||
|
||||
Tests run automatically on pull requests via GitHub Actions:
|
||||
|
||||
### Automated Testing Workflow
|
||||
|
||||
When a pull request is created or updated with changes to `apiserver/**` files, the `test-pull-request.yml` workflow automatically:
|
||||
|
||||
1. **Sets up test environment**: PostgreSQL 14, Redis 7, Python 3.11
|
||||
2. **Runs unit tests**: Fast, isolated component tests with coverage
|
||||
3. **Runs contract tests**: API endpoint verification
|
||||
4. **Generates coverage reports**: Enforces 90% threshold with HTML, terminal, and XML formats
|
||||
5. **Uploads to Codecov**: If token is configured
|
||||
|
||||
### CI Environment Variables
|
||||
|
||||
The CI automatically configures comprehensive environment variables including:
|
||||
- Database and Redis connections
|
||||
- Security settings (disabled for testing)
|
||||
- Base URLs for all components
|
||||
- File upload and storage settings
|
||||
- External service configurations (mocked)
|
||||
|
||||
## API vs App Endpoints
|
||||
|
||||
Plane has two types of API endpoints:
|
||||
@@ -32,6 +55,8 @@ Plane has two types of API endpoints:
|
||||
|
||||
## Running Tests
|
||||
|
||||
### Local Testing
|
||||
|
||||
To run all tests:
|
||||
|
||||
```bash
|
||||
@@ -54,20 +79,19 @@ python -m pytest plane/tests/contract/app/
|
||||
python -m pytest plane/tests/smoke/
|
||||
```
|
||||
|
||||
For convenience, we also provide a helper script:
|
||||
### Using the Test Runner
|
||||
|
||||
For convenience, we provide helper scripts:
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
./run_tests.py
|
||||
# Using Python script directly
|
||||
python run_tests.py --coverage --verbose # Full test suite with coverage
|
||||
python run_tests.py -u -v # Unit tests only
|
||||
python run_tests.py -c -v # Contract tests only
|
||||
python run_tests.py -p -v # Parallel execution
|
||||
|
||||
# Run only unit tests
|
||||
./run_tests.py -u
|
||||
|
||||
# Run contract tests with coverage report
|
||||
./run_tests.py -c -o
|
||||
|
||||
# Run tests in parallel
|
||||
./run_tests.py -p
|
||||
# Using shell wrapper
|
||||
./run_tests.sh --coverage --verbose # Full test suite with coverage
|
||||
```
|
||||
|
||||
## Fixtures
|
||||
@@ -134,9 +158,30 @@ Generate a coverage report with:
|
||||
|
||||
```bash
|
||||
python -m pytest --cov=plane --cov-report=term --cov-report=html
|
||||
# Or using the test runner
|
||||
python run_tests.py --coverage
|
||||
```
|
||||
|
||||
This creates an HTML report in the `htmlcov/` directory.
|
||||
This creates an HTML report in the `htmlcov/` directory and enforces the 90% coverage threshold.
|
||||
|
||||
## CI Troubleshooting
|
||||
|
||||
### Common CI Issues
|
||||
|
||||
1. **Test failures**: Check the GitHub Actions logs for specific error messages
|
||||
2. **Coverage below threshold**: Add tests for uncovered code
|
||||
3. **Database connection issues**: Ensure PostgreSQL service is healthy in CI
|
||||
4. **Redis connection issues**: Ensure Redis service is healthy in CI
|
||||
|
||||
### Local Setup for CI Testing
|
||||
|
||||
Make sure you have the test dependencies installed:
|
||||
|
||||
```bash
|
||||
pip install -r requirements/test.txt
|
||||
```
|
||||
|
||||
Set up your local environment with PostgreSQL and Redis, or use the provided Docker setup.
|
||||
|
||||
## Migration from Old Tests
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ def user_data():
|
||||
"email": "test@plane.so",
|
||||
"password": "test-password",
|
||||
"first_name": "Test",
|
||||
"last_name": "User"
|
||||
"last_name": "User",
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ def create_user(db, user_data):
|
||||
user = User.objects.create(
|
||||
email=user_data["email"],
|
||||
first_name=user_data["first_name"],
|
||||
last_name=user_data["last_name"]
|
||||
last_name=user_data["last_name"],
|
||||
)
|
||||
user.set_password(user_data["password"])
|
||||
user.save()
|
||||
@@ -75,4 +75,4 @@ def plane_server(live_server):
|
||||
Renamed version of live_server fixture to avoid name clashes.
|
||||
Returns a live Django server for testing HTTP requests.
|
||||
"""
|
||||
return live_server
|
||||
return live_server
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# base requirements
|
||||
|
||||
# django
|
||||
Django==4.2.22
|
||||
Django==4.2.21
|
||||
# rest framework
|
||||
djangorestframework==3.15.2
|
||||
# postgres
|
||||
|
||||
@@ -9,4 +9,4 @@ factory-boy==3.3.0
|
||||
freezegun==1.2.2
|
||||
coverage==7.2.7
|
||||
httpx==0.24.1
|
||||
requests==2.32.4
|
||||
requests==2.32.2
|
||||
+18
-27
@@ -6,36 +6,20 @@ import sys
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Run Plane tests")
|
||||
parser.add_argument("-u", "--unit", action="store_true", help="Run unit tests only")
|
||||
parser.add_argument(
|
||||
"-u", "--unit",
|
||||
action="store_true",
|
||||
help="Run unit tests only"
|
||||
"-c", "--contract", action="store_true", help="Run contract tests only"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c", "--contract",
|
||||
action="store_true",
|
||||
help="Run contract tests only"
|
||||
"-s", "--smoke", action="store_true", help="Run smoke tests only"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s", "--smoke",
|
||||
action="store_true",
|
||||
help="Run smoke tests only"
|
||||
"-o", "--coverage", action="store_true", help="Generate coverage report"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o", "--coverage",
|
||||
action="store_true",
|
||||
help="Generate coverage report"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p", "--parallel",
|
||||
action="store_true",
|
||||
help="Run tests in parallel"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v", "--verbose",
|
||||
action="store_true",
|
||||
help="Verbose output"
|
||||
"-p", "--parallel", action="store_true", help="Run tests in parallel"
|
||||
)
|
||||
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Build command
|
||||
@@ -56,7 +40,14 @@ def main():
|
||||
|
||||
# Add coverage
|
||||
if args.coverage:
|
||||
cmd.extend(["--cov=plane", "--cov-report=term", "--cov-report=html"])
|
||||
cmd.extend(
|
||||
[
|
||||
"--cov=plane",
|
||||
"--cov-report=term",
|
||||
"--cov-report=html",
|
||||
"--cov-report=xml",
|
||||
]
|
||||
)
|
||||
|
||||
# Add parallel
|
||||
if args.parallel:
|
||||
@@ -71,10 +62,10 @@ def main():
|
||||
|
||||
# Print command
|
||||
print(f"Running: {' '.join(cmd)}")
|
||||
|
||||
|
||||
# Execute command
|
||||
result = subprocess.run(cmd)
|
||||
|
||||
|
||||
# Check coverage thresholds if coverage is enabled
|
||||
if args.coverage:
|
||||
print("Checking coverage thresholds...")
|
||||
@@ -83,9 +74,9 @@ def main():
|
||||
if coverage_result.returncode != 0:
|
||||
print("Coverage below threshold (90%)")
|
||||
sys.exit(coverage_result.returncode)
|
||||
|
||||
|
||||
sys.exit(result.returncode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Updates on {{entity_type}}</title>
|
||||
<title>Updates on issue</title>
|
||||
<style type="text/css" emogrify="no"> html { font-family: system-ui; } p, h1, h2, h3, h4, ol, ul { margin: 0; } h-full { height: 100%; } a:hover { color: #3358d4 !important; } </style>
|
||||
<style> *[class="gmail-fix"] { display: none !important; } </style>
|
||||
<style type="text/css" emogrify="no"> @media (max-width: 600px) { .gmx-killpill { content: " \03D1"; } } </style>
|
||||
@@ -37,7 +37,7 @@
|
||||
{% else %}
|
||||
<p style="font-size: 1rem;color: #1f2d5c; line-height: 28px"> {{summary}} <span style="font-size: 1rem; font-weight: 700; line-height: 28px"> {% if data|length > 0 %} {{ data.0.actor_detail.first_name}} {{data.0.actor_detail.last_name}} {% else %} {{ comments.0.actor_detail.first_name}} {{comments.0.actor_detail.last_name}} {% endif %} </span>and others. </p>
|
||||
{% endif %} <!-- {% if actors_involved == 1 %} {% if data|length > 0 and comments|length == 0 %} <p style="font-size: 1rem;color: #1f2d5c; line-height: 28px"> <span style="font-size: 1rem; font-weight: 700; line-height: 28px"> {{ data.0.actor_detail.first_name}} {{data.0.actor_detail.last_name }} </span> made {{total_updates}} {% if total_updates > 1 %}updates{% else %}update{% endif %} to the issue. </p> {% elif data|length == 0 and comments|length > 0 %} <p style="font-size: 1rem;color: #1f2d5c; line-height: 28px"> <span style="font-size: 1rem; font-weight: 700; line-height: 28px"> {{ comments.0.actor_detail.first_name}} {{comments.0.actor_detail.last_name }} </span> added {{total_comments}} new {% if total_comments > 1 %}comments{% else %}comment{% endif %}. </p> {% elif data|length > 0 and comments|length > 0 %} <p style="font-size: 1rem;color: #1f2d5c; line-height: 28px"> <span style="font-size: 1rem; font-weight: 700; line-height: 28px"> {{ data.0.actor_detail.first_name}} {{data.0.actor_detail.last_name }} </span> made {{total_updates}} {% if total_updates > 1 %}updates{% else %}update{% endif %} and added {{total_comments}} new {% if total_comments > 1 %}comments{% else %}comment{% endif %} on the issue. </p> {% endif %} {% else %} <p style="font-size: 1rem;color: #1f2d5c; line-height: 28px"> There are {{ total_updates }} new updates and {{total_comments}} new comments on the issue. </p> {% endif %} --> {% for update in data %} {% if update.changes.name %} <!-- Issue title updated -->
|
||||
<p style="font-size: 1rem; line-height: 28px; color: #1f2d5c"> The {{entity_type}} title has been updated to {{ issue.name}} </p>
|
||||
<p style="font-size: 1rem; line-height: 28px; color: #1f2d5c"> The issue title has been updated to {{ issue.name}} </p>
|
||||
{% endif %} <!-- Outer update Box start --> {% if data %}
|
||||
<div style=" background-color: #f7f9ff; border-radius: 8px; border-style: solid; border-width: 1px; border-color: #c1d0ff; padding: 20px; margin-top: 15px; max-width: 100%; " >
|
||||
<!-- Block Heading -->
|
||||
@@ -224,7 +224,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<a href="{{ issue_url }}" style="text-decoration: none;">
|
||||
<div style=" max-width: min-content; white-space: nowrap; background-color: #3e63dd; padding: 10px 15px; border: 1px solid #2f4ba8; border-radius: 4px; margin-top: 15px; cursor: pointer; font-size: 0.8rem; color: white; " > View {{entity_type}} </div>
|
||||
<div style=" max-width: min-content; white-space: nowrap; background-color: #3e63dd; padding: 10px 15px; border: 1px solid #2f4ba8; border-radius: 4px; margin-top: 15px; cursor: pointer; font-size: 0.8rem; color: white; " > View issue </div>
|
||||
</a>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
@@ -232,7 +232,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
<div style="font-size: 0.8rem; color: #1c2024">
|
||||
This email was sent to <a href="mailto:{{receiver.email}}" style="color: #3a5bc7; font-weight: 500; text-decoration: none" >{{ receiver.email }}.</a > If you'd rather not receive this kind of email, <a href="{{ issue_url }}" style="color: #3a5bc7; text-decoration: none" >you can unsubscribe to the {{entity_type}}</a > or <a href="{{ user_preference }}" style="color: #3a5bc7; text-decoration: none" >manage your email preferences</a >. <!-- Github | LinkedIn | Twitter -->
|
||||
This email was sent to <a href="mailto:{{receiver.email}}" style="color: #3a5bc7; font-weight: 500; text-decoration: none" >{{ receiver.email }}.</a > If you'd rather not receive this kind of email, <a href="{{ issue_url }}" style="color: #3a5bc7; text-decoration: none" >you can unsubscribe to the issue</a > or <a href="{{ user_preference }}" style="color: #3a5bc7; text-decoration: none" >manage your email preferences</a >. <!-- Github | LinkedIn | Twitter -->
|
||||
<div style="margin-top: 60px; float: right"> <a href="https://github.com/makeplane" target="_blank" style="margin-left: 10px; text-decoration: none" > <img src="https://creative-assets.mailinblue.com/editor/social-icons/rounded_colored/github_32px.png" width="25" height="25" border="0" style="display: inline-block" /> </a> <a href="https://www.linkedin.com/company/planepowers/" target="_blank" style="margin-left: 10px; text-decoration: none" > <img src="https://creative-assets.mailinblue.com/editor/social-icons/rounded_colored/linkedin_32px.png" width="25" height="25" border="0" style="display: inline-block" /> </a> <a href="https://twitter.com/planepowers" target="_blank" style="margin-left: 10px; text-decoration: none" > <img src="https://creative-assets.mailinblue.com/editor/social-icons/rounded_colored/twitter_32px.png" width="25" height="25" border="0" style="display: inline-block" /> </a> </div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -486,7 +486,7 @@ When you want to restore the previously backed-up data, follow the instructions
|
||||
1. Download the restore script using the command below. We suggest downloading it in the same folder as `setup.sh`.
|
||||
|
||||
```bash
|
||||
curl -fsSL -o restore.sh https://github.com/makeplane/plane/releases/latest/download/restore.sh
|
||||
curl -fsSL -o restore.sh https://raw.githubusercontent.com/makeplane/plane/master/deploy/selfhost/restore.sh
|
||||
chmod +x restore.sh
|
||||
```
|
||||
|
||||
@@ -529,31 +529,6 @@ When you want to restore the previously backed-up data, follow the instructions
|
||||
|
||||
---
|
||||
|
||||
### Restore for Commercial Air-Gapped (Docker Compose)
|
||||
|
||||
When you want to restore the previously backed-up data on Plane Commercial Air-Gapped version, follow the instructions below.
|
||||
|
||||
1. Download the restore script using the command below
|
||||
|
||||
```bash
|
||||
curl -fsSL -o restore-airgapped.sh https://github.com/makeplane/plane/releases/latest/download/restore-airgapped.sh
|
||||
chmod +x restore-airgapped.sh
|
||||
```
|
||||
|
||||
1. Copy the backup folder and the `restore-airgapped.sh` to `Commercial Airgapped Edition` server
|
||||
|
||||
1. Make sure that Plane Commercial (Airgapped) is extracted and ready to get started. In case it is running, you would need to stop that.
|
||||
|
||||
1. Execute the command below to restore your data.
|
||||
|
||||
```bash
|
||||
./restore-airgapped.sh <path to backup folder containing *.tar.gz files>
|
||||
```
|
||||
|
||||
1. After restoration, you are ready to start Plane Commercial (Airgapped) will all your previously saved data.
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary><h2>Upgrading from v0.13.2 to v0.14.x</h2></summary>
|
||||
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
#!/bin/bash
|
||||
+set -euo pipefail
|
||||
|
||||
function print_header() {
|
||||
clear
|
||||
|
||||
cat <<"EOF"
|
||||
--------------------------------------------
|
||||
____ _ /////////
|
||||
| _ \| | __ _ _ __ ___ /////////
|
||||
| |_) | |/ _` | '_ \ / _ \ ///// /////
|
||||
| __/| | (_| | | | | __/ ///// /////
|
||||
|_| |_|\__,_|_| |_|\___| ////
|
||||
////
|
||||
--------------------------------------------
|
||||
Project management tool from the future
|
||||
--------------------------------------------
|
||||
EOF
|
||||
}
|
||||
|
||||
function restoreData() {
|
||||
|
||||
echo ""
|
||||
echo "****************************************************"
|
||||
echo "We are about to restore your data from the backup files."
|
||||
echo "****************************************************"
|
||||
echo ""
|
||||
|
||||
# set the backup folder path
|
||||
BACKUP_FOLDER=${1}
|
||||
|
||||
if [ -z "$BACKUP_FOLDER" ]; then
|
||||
BACKUP_FOLDER="$PWD/backup"
|
||||
read -p "Enter the backup folder path [$BACKUP_FOLDER]: " BACKUP_FOLDER
|
||||
if [ -z "$BACKUP_FOLDER" ]; then
|
||||
BACKUP_FOLDER="$PWD/backup"
|
||||
fi
|
||||
fi
|
||||
|
||||
# check if the backup folder exists
|
||||
if [ ! -d "$BACKUP_FOLDER" ]; then
|
||||
echo "Error: Backup folder not found at $BACKUP_FOLDER"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# check if there are any .tar.gz files in the backup folder
|
||||
if ! ls "$BACKUP_FOLDER"/*.tar.gz 1> /dev/null 2>&1; then
|
||||
echo "Error: Backup folder does not contain .tar.gz files"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Using backup folder: $BACKUP_FOLDER"
|
||||
echo ""
|
||||
|
||||
# ask for current install path
|
||||
AIRGAPPED_INSTALL_PATH="$HOME/planeairgapped"
|
||||
read -p "Enter the airgapped instance install path [$AIRGAPPED_INSTALL_PATH]: " AIRGAPPED_INSTALL_PATH
|
||||
if [ -z "$AIRGAPPED_INSTALL_PATH" ]; then
|
||||
AIRGAPPED_INSTALL_PATH="$HOME/planeairgapped"
|
||||
fi
|
||||
|
||||
# check if the airgapped instance install path exists
|
||||
if [ ! -d "$AIRGAPPED_INSTALL_PATH" ]; then
|
||||
echo "Error: Airgapped instance install path not found at $AIRGAPPED_INSTALL_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Using airgapped instance install path: $AIRGAPPED_INSTALL_PATH"
|
||||
echo ""
|
||||
|
||||
# check if the docker-compose.yaml exists
|
||||
if [ ! -f "$AIRGAPPED_INSTALL_PATH/docker-compose.yml" ]; then
|
||||
echo "Error: docker-compose.yml not found at $AIRGAPPED_INSTALL_PATH/docker-compose.yml"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local dockerServiceStatus
|
||||
if command -v jq &> /dev/null; then
|
||||
dockerServiceStatus=$($COMPOSE_CMD ls --filter name=plane-airgapped --format=json | jq -r .[0].Status)
|
||||
else
|
||||
dockerServiceStatus=$($COMPOSE_CMD ls --filter name=plane-airgapped | grep -o "running" | head -n 1)
|
||||
fi
|
||||
|
||||
if [[ $dockerServiceStatus == "running" ]]; then
|
||||
echo "Plane Airgapped is running. Please STOP the Plane Airgapped before restoring data."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CURRENT_USER_ID=$(id -u)
|
||||
CURRENT_GROUP_ID=$(id -g)
|
||||
|
||||
# if the data folder not exists, create it
|
||||
if [ ! -d "$AIRGAPPED_INSTALL_PATH/data" ]; then
|
||||
mkdir -p "$AIRGAPPED_INSTALL_PATH/data"
|
||||
chown -R $CURRENT_USER_ID:$CURRENT_GROUP_ID "$AIRGAPPED_INSTALL_PATH/data"
|
||||
fi
|
||||
|
||||
for BACKUP_FILE in "$BACKUP_FOLDER/*.tar.gz"; do
|
||||
if [ -e "$BACKUP_FILE" ]; then
|
||||
|
||||
# get the basefilename without the extension
|
||||
BASE_FILE_NAME=$(basename "$BACKUP_FILE" ".tar.gz")
|
||||
|
||||
# extract the restoreFile to the airgapped instance install path
|
||||
echo "Restoring $BASE_FILE_NAME"
|
||||
rm -rf "$AIRGAPPED_INSTALL_PATH/data/$BASE_FILE_NAME" || true
|
||||
|
||||
tar -xvzf "$BACKUP_FILE" -C "$AIRGAPPED_INSTALL_PATH/data/"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to extract $BACKUP_FILE"
|
||||
exit 1
|
||||
fi
|
||||
chown -R $CURRENT_USER_ID:$CURRENT_GROUP_ID "$AIRGAPPED_INSTALL_PATH/data/$BASE_FILE_NAME"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Failed to change ownership of $AIRGAPPED_INSTALL_PATH/data/$BASE_FILE_NAME"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "No .tar.gz files found in the current directory."
|
||||
echo ""
|
||||
echo "Please provide the path to the backup file."
|
||||
echo ""
|
||||
echo "Usage: $0 /path/to/backup"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Restore completed successfully."
|
||||
echo ""
|
||||
}
|
||||
|
||||
# if docker-compose is installed
|
||||
if command -v docker-compose &> /dev/null
|
||||
then
|
||||
COMPOSE_CMD="docker-compose"
|
||||
else
|
||||
COMPOSE_CMD="docker compose"
|
||||
fi
|
||||
|
||||
print_header
|
||||
restoreData "$@"
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "live",
|
||||
"version": "0.26.1",
|
||||
"version": "0.26.0",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "A realtime collaborative server powers Plane's rich text editor",
|
||||
"main": "./src/server.ts",
|
||||
@@ -58,6 +58,6 @@
|
||||
"nodemon": "^3.1.7",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsup": "8.4.0",
|
||||
"typescript": "5.8.3"
|
||||
"typescript": "5.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -2,7 +2,7 @@
|
||||
"name": "plane",
|
||||
"description": "Open-source project management that unlocks customer value",
|
||||
"repository": "https://github.com/makeplane/plane.git",
|
||||
"version": "0.26.1",
|
||||
"version": "0.26.0",
|
||||
"license": "AGPL-3.0",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
@@ -24,15 +24,14 @@
|
||||
"devDependencies": {
|
||||
"prettier": "latest",
|
||||
"prettier-plugin-tailwindcss": "^0.5.4",
|
||||
"turbo": "^2.5.4"
|
||||
"turbo": "^2.5.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"nanoid": "3.3.8",
|
||||
"esbuild": "0.25.0",
|
||||
"@babel/helpers": "7.26.10",
|
||||
"@babel/runtime": "7.26.10",
|
||||
"chokidar": "3.6.0",
|
||||
"tar-fs": "3.0.9"
|
||||
"chokidar": "3.6.0"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@plane/constants",
|
||||
"version": "0.26.1",
|
||||
"version": "0.26.0",
|
||||
"private": true,
|
||||
"main": "./src/index.ts",
|
||||
"license": "AGPL-3.0"
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
export const AI_EDITOR_TASKS = {
|
||||
ASK_ANYTHING: "ASK_ANYTHING",
|
||||
} as const;
|
||||
|
||||
export type AI_EDITOR_TASKS = typeof AI_EDITOR_TASKS[keyof typeof AI_EDITOR_TASKS];
|
||||
export enum AI_EDITOR_TASKS {
|
||||
ASK_ANYTHING = "ASK_ANYTHING",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import { TAnalyticsTabsV2Base } from "@plane/types";
|
||||
import { ChartXAxisProperty, ChartYAxisMetric } from "../chart";
|
||||
|
||||
export const insightsFields: Record<TAnalyticsTabsV2Base, string[]> = {
|
||||
overview: [
|
||||
"total_users",
|
||||
"total_admins",
|
||||
"total_members",
|
||||
"total_guests",
|
||||
"total_projects",
|
||||
"total_work_items",
|
||||
"total_cycles",
|
||||
"total_intake",
|
||||
],
|
||||
"work-items": [
|
||||
"total_work_items",
|
||||
"started_work_items",
|
||||
"backlog_work_items",
|
||||
"un_started_work_items",
|
||||
"completed_work_items",
|
||||
],
|
||||
};
|
||||
|
||||
export const ANALYTICS_V2_DURATION_FILTER_OPTIONS = [
|
||||
{
|
||||
name: "Yesterday",
|
||||
value: "yesterday",
|
||||
},
|
||||
{
|
||||
name: "Last 7 days",
|
||||
value: "last_7_days",
|
||||
},
|
||||
{
|
||||
name: "Last 30 days",
|
||||
value: "last_30_days",
|
||||
},
|
||||
{
|
||||
name: "Last 3 months",
|
||||
value: "last_3_months",
|
||||
},
|
||||
];
|
||||
|
||||
export const ANALYTICS_V2_X_AXIS_VALUES: { value: ChartXAxisProperty; label: string }[] = [
|
||||
{
|
||||
value: ChartXAxisProperty.STATES,
|
||||
label: "State name",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.STATE_GROUPS,
|
||||
label: "State group",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.PRIORITY,
|
||||
label: "Priority",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.LABELS,
|
||||
label: "Label",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.ASSIGNEES,
|
||||
label: "Assignee",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.ESTIMATE_POINTS,
|
||||
label: "Estimate point",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.CYCLES,
|
||||
label: "Cycle",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.MODULES,
|
||||
label: "Module",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.COMPLETED_AT,
|
||||
label: "Completed date",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.TARGET_DATE,
|
||||
label: "Due date",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.START_DATE,
|
||||
label: "Start date",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.CREATED_AT,
|
||||
label: "Created date",
|
||||
},
|
||||
];
|
||||
|
||||
export const ANALYTICS_V2_Y_AXIS_VALUES: { value: ChartYAxisMetric; label: string }[] = [
|
||||
{
|
||||
value: ChartYAxisMetric.WORK_ITEM_COUNT,
|
||||
label: "Work item",
|
||||
},
|
||||
{
|
||||
value: ChartYAxisMetric.ESTIMATE_POINT_COUNT,
|
||||
label: "Estimate",
|
||||
},
|
||||
];
|
||||
|
||||
export const ANALYTICS_V2_DATE_KEYS = ["completed_at", "target_date", "start_date", "created_at"];
|
||||
@@ -0,0 +1,81 @@
|
||||
// types
|
||||
import { TXAxisValues, TYAxisValues } from "@plane/types";
|
||||
|
||||
export const ANALYTICS_TABS = [
|
||||
{
|
||||
key: "scope_and_demand",
|
||||
i18n_title: "workspace_analytics.tabs.scope_and_demand",
|
||||
},
|
||||
{ key: "custom", i18n_title: "workspace_analytics.tabs.custom" },
|
||||
];
|
||||
|
||||
export const ANALYTICS_X_AXIS_VALUES: { value: TXAxisValues; label: string }[] =
|
||||
[
|
||||
{
|
||||
value: "state_id",
|
||||
label: "State name",
|
||||
},
|
||||
{
|
||||
value: "state__group",
|
||||
label: "State group",
|
||||
},
|
||||
{
|
||||
value: "priority",
|
||||
label: "Priority",
|
||||
},
|
||||
{
|
||||
value: "labels__id",
|
||||
label: "Label",
|
||||
},
|
||||
{
|
||||
value: "assignees__id",
|
||||
label: "Assignee",
|
||||
},
|
||||
{
|
||||
value: "estimate_point__value",
|
||||
label: "Estimate point",
|
||||
},
|
||||
{
|
||||
value: "issue_cycle__cycle_id",
|
||||
label: "Cycle",
|
||||
},
|
||||
{
|
||||
value: "issue_module__module_id",
|
||||
label: "Module",
|
||||
},
|
||||
{
|
||||
value: "completed_at",
|
||||
label: "Completed date",
|
||||
},
|
||||
{
|
||||
value: "target_date",
|
||||
label: "Due date",
|
||||
},
|
||||
{
|
||||
value: "start_date",
|
||||
label: "Start date",
|
||||
},
|
||||
{
|
||||
value: "created_at",
|
||||
label: "Created date",
|
||||
},
|
||||
];
|
||||
|
||||
export const ANALYTICS_Y_AXIS_VALUES: { value: TYAxisValues; label: string }[] =
|
||||
[
|
||||
{
|
||||
value: "issue_count",
|
||||
label: "Work item Count",
|
||||
},
|
||||
{
|
||||
value: "estimate",
|
||||
label: "Estimate",
|
||||
},
|
||||
];
|
||||
|
||||
export const ANALYTICS_DATE_KEYS = [
|
||||
"completed_at",
|
||||
"target_date",
|
||||
"start_date",
|
||||
"created_at",
|
||||
];
|
||||
@@ -1,178 +0,0 @@
|
||||
import { TAnalyticsTabsBase } from "@plane/types";
|
||||
import { ChartXAxisProperty, ChartYAxisMetric } from "../chart";
|
||||
|
||||
export interface IInsightField {
|
||||
key: string;
|
||||
i18nKey: string;
|
||||
i18nProps?: {
|
||||
entity?: string;
|
||||
entityPlural?: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
export const insightsFields: Record<TAnalyticsTabsBase, IInsightField[]> = {
|
||||
overview: [
|
||||
{
|
||||
key: "total_users",
|
||||
i18nKey: "workspace_analytics.total",
|
||||
i18nProps: {
|
||||
entity: "common.users",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "total_admins",
|
||||
i18nKey: "workspace_analytics.total",
|
||||
i18nProps: {
|
||||
entity: "common.admins",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "total_members",
|
||||
i18nKey: "workspace_analytics.total",
|
||||
i18nProps: {
|
||||
entity: "common.members",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "total_guests",
|
||||
i18nKey: "workspace_analytics.total",
|
||||
i18nProps: {
|
||||
entity: "common.guests",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "total_projects",
|
||||
i18nKey: "workspace_analytics.total",
|
||||
i18nProps: {
|
||||
entity: "common.projects",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "total_work_items",
|
||||
i18nKey: "workspace_analytics.total",
|
||||
i18nProps: {
|
||||
entity: "common.work_items",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "total_cycles",
|
||||
i18nKey: "workspace_analytics.total",
|
||||
i18nProps: {
|
||||
entity: "common.cycles",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "total_intake",
|
||||
i18nKey: "workspace_analytics.total",
|
||||
i18nProps: {
|
||||
entity: "sidebar.intake",
|
||||
},
|
||||
},
|
||||
],
|
||||
"work-items": [
|
||||
{
|
||||
key: "total_work_items",
|
||||
i18nKey: "workspace_analytics.total",
|
||||
},
|
||||
{
|
||||
key: "started_work_items",
|
||||
i18nKey: "workspace_analytics.started_work_items",
|
||||
},
|
||||
{
|
||||
key: "backlog_work_items",
|
||||
i18nKey: "workspace_analytics.backlog_work_items",
|
||||
},
|
||||
{
|
||||
key: "un_started_work_items",
|
||||
i18nKey: "workspace_analytics.un_started_work_items",
|
||||
},
|
||||
{
|
||||
key: "completed_work_items",
|
||||
i18nKey: "workspace_analytics.completed_work_items",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const ANALYTICS_DURATION_FILTER_OPTIONS = [
|
||||
{
|
||||
name: "Yesterday",
|
||||
value: "yesterday",
|
||||
},
|
||||
{
|
||||
name: "Last 7 days",
|
||||
value: "last_7_days",
|
||||
},
|
||||
{
|
||||
name: "Last 30 days",
|
||||
value: "last_30_days",
|
||||
},
|
||||
{
|
||||
name: "Last 3 months",
|
||||
value: "last_3_months",
|
||||
},
|
||||
];
|
||||
|
||||
export const ANALYTICS_X_AXIS_VALUES: { value: ChartXAxisProperty; label: string }[] = [
|
||||
{
|
||||
value: ChartXAxisProperty.STATES,
|
||||
label: "State name",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.STATE_GROUPS,
|
||||
label: "State group",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.PRIORITY,
|
||||
label: "Priority",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.LABELS,
|
||||
label: "Label",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.ASSIGNEES,
|
||||
label: "Assignee",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.ESTIMATE_POINTS,
|
||||
label: "Estimate point",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.CYCLES,
|
||||
label: "Cycle",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.MODULES,
|
||||
label: "Module",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.COMPLETED_AT,
|
||||
label: "Completed date",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.TARGET_DATE,
|
||||
label: "Due date",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.START_DATE,
|
||||
label: "Start date",
|
||||
},
|
||||
{
|
||||
value: ChartXAxisProperty.CREATED_AT,
|
||||
label: "Created date",
|
||||
},
|
||||
];
|
||||
|
||||
export const ANALYTICS_Y_AXIS_VALUES: { value: ChartYAxisMetric; label: string }[] = [
|
||||
{
|
||||
value: ChartYAxisMetric.WORK_ITEM_COUNT,
|
||||
label: "Work item",
|
||||
},
|
||||
{
|
||||
value: ChartYAxisMetric.ESTIMATE_POINT_COUNT,
|
||||
label: "Estimate",
|
||||
},
|
||||
];
|
||||
|
||||
export const ANALYTICS_V2_DATE_KEYS = ["completed_at", "target_date", "start_date", "created_at"];
|
||||
+100
-116
@@ -1,11 +1,9 @@
|
||||
export const E_PASSWORD_STRENGTH = {
|
||||
EMPTY: "empty",
|
||||
LENGTH_NOT_VALID: "length_not_valid",
|
||||
STRENGTH_NOT_VALID: "strength_not_valid",
|
||||
STRENGTH_VALID: "strength_valid",
|
||||
} as const;
|
||||
|
||||
export type E_PASSWORD_STRENGTH = typeof E_PASSWORD_STRENGTH[keyof typeof E_PASSWORD_STRENGTH];
|
||||
export enum E_PASSWORD_STRENGTH {
|
||||
EMPTY = "empty",
|
||||
LENGTH_NOT_VALID = "length_not_valid",
|
||||
STRENGTH_NOT_VALID = "strength_not_valid",
|
||||
STRENGTH_VALID = "strength_valid",
|
||||
}
|
||||
|
||||
export const PASSWORD_MIN_LENGTH = 8;
|
||||
|
||||
@@ -33,51 +31,41 @@ export const SPACE_PASSWORD_CRITERIA = [
|
||||
// },
|
||||
];
|
||||
|
||||
export const EAuthPageTypes = {
|
||||
PUBLIC: "PUBLIC",
|
||||
NON_AUTHENTICATED: "NON_AUTHENTICATED",
|
||||
SET_PASSWORD: "SET_PASSWORD",
|
||||
ONBOARDING: "ONBOARDING",
|
||||
AUTHENTICATED: "AUTHENTICATED",
|
||||
} as const;
|
||||
export enum EAuthPageTypes {
|
||||
PUBLIC = "PUBLIC",
|
||||
NON_AUTHENTICATED = "NON_AUTHENTICATED",
|
||||
SET_PASSWORD = "SET_PASSWORD",
|
||||
ONBOARDING = "ONBOARDING",
|
||||
AUTHENTICATED = "AUTHENTICATED",
|
||||
}
|
||||
|
||||
export type EAuthPageTypes = typeof EAuthPageTypes[keyof typeof EAuthPageTypes];
|
||||
export enum EPageTypes {
|
||||
INIT = "INIT",
|
||||
PUBLIC = "PUBLIC",
|
||||
NON_AUTHENTICATED = "NON_AUTHENTICATED",
|
||||
ONBOARDING = "ONBOARDING",
|
||||
AUTHENTICATED = "AUTHENTICATED",
|
||||
}
|
||||
|
||||
export const EPageTypes = {
|
||||
INIT: "INIT",
|
||||
PUBLIC: "PUBLIC",
|
||||
NON_AUTHENTICATED: "NON_AUTHENTICATED",
|
||||
ONBOARDING: "ONBOARDING",
|
||||
AUTHENTICATED: "AUTHENTICATED",
|
||||
} as const;
|
||||
export enum EAuthModes {
|
||||
SIGN_IN = "SIGN_IN",
|
||||
SIGN_UP = "SIGN_UP",
|
||||
}
|
||||
|
||||
export type EPageTypes = typeof EPageTypes[keyof typeof EPageTypes];
|
||||
export enum EAuthSteps {
|
||||
EMAIL = "EMAIL",
|
||||
PASSWORD = "PASSWORD",
|
||||
UNIQUE_CODE = "UNIQUE_CODE",
|
||||
}
|
||||
|
||||
export const EAuthModes = {
|
||||
SIGN_IN: "SIGN_IN",
|
||||
SIGN_UP: "SIGN_UP",
|
||||
} as const;
|
||||
|
||||
export type EAuthModes = typeof EAuthModes[keyof typeof EAuthModes];
|
||||
|
||||
export const EAuthSteps = {
|
||||
EMAIL: "EMAIL",
|
||||
PASSWORD: "PASSWORD",
|
||||
UNIQUE_CODE: "UNIQUE_CODE",
|
||||
} as const;
|
||||
|
||||
export type EAuthSteps = typeof EAuthSteps[keyof typeof EAuthSteps];
|
||||
|
||||
export const EErrorAlertType = {
|
||||
BANNER_ALERT: "BANNER_ALERT",
|
||||
TOAST_ALERT: "TOAST_ALERT",
|
||||
INLINE_FIRST_NAME: "INLINE_FIRST_NAME",
|
||||
INLINE_EMAIL: "INLINE_EMAIL",
|
||||
INLINE_PASSWORD: "INLINE_PASSWORD",
|
||||
INLINE_EMAIL_CODE: "INLINE_EMAIL_CODE",
|
||||
} as const;
|
||||
|
||||
export type EErrorAlertType = typeof EErrorAlertType[keyof typeof EErrorAlertType];
|
||||
export enum EErrorAlertType {
|
||||
BANNER_ALERT = "BANNER_ALERT",
|
||||
TOAST_ALERT = "TOAST_ALERT",
|
||||
INLINE_FIRST_NAME = "INLINE_FIRST_NAME",
|
||||
INLINE_EMAIL = "INLINE_EMAIL",
|
||||
INLINE_PASSWORD = "INLINE_PASSWORD",
|
||||
INLINE_EMAIL_CODE = "INLINE_EMAIL_CODE",
|
||||
}
|
||||
|
||||
export type TAuthErrorInfo = {
|
||||
type: EErrorAlertType;
|
||||
@@ -86,83 +74,79 @@ export type TAuthErrorInfo = {
|
||||
message: any;
|
||||
};
|
||||
|
||||
export const EAdminAuthErrorCodes = {
|
||||
export enum EAdminAuthErrorCodes {
|
||||
// Admin
|
||||
ADMIN_ALREADY_EXIST: "5150",
|
||||
REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME: "5155",
|
||||
INVALID_ADMIN_EMAIL: "5160",
|
||||
INVALID_ADMIN_PASSWORD: "5165",
|
||||
REQUIRED_ADMIN_EMAIL_PASSWORD: "5170",
|
||||
ADMIN_AUTHENTICATION_FAILED: "5175",
|
||||
ADMIN_USER_ALREADY_EXIST: "5180",
|
||||
ADMIN_USER_DOES_NOT_EXIST: "5185",
|
||||
ADMIN_USER_DEACTIVATED: "5190",
|
||||
} as const;
|
||||
ADMIN_ALREADY_EXIST = "5150",
|
||||
REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME = "5155",
|
||||
INVALID_ADMIN_EMAIL = "5160",
|
||||
INVALID_ADMIN_PASSWORD = "5165",
|
||||
REQUIRED_ADMIN_EMAIL_PASSWORD = "5170",
|
||||
ADMIN_AUTHENTICATION_FAILED = "5175",
|
||||
ADMIN_USER_ALREADY_EXIST = "5180",
|
||||
ADMIN_USER_DOES_NOT_EXIST = "5185",
|
||||
ADMIN_USER_DEACTIVATED = "5190",
|
||||
}
|
||||
|
||||
export type EAdminAuthErrorCodes = typeof EAdminAuthErrorCodes[keyof typeof EAdminAuthErrorCodes];
|
||||
|
||||
export const EAuthErrorCodes = {
|
||||
export enum EAuthErrorCodes {
|
||||
// Global
|
||||
INSTANCE_NOT_CONFIGURED: "5000",
|
||||
INVALID_EMAIL: "5005",
|
||||
EMAIL_REQUIRED: "5010",
|
||||
SIGNUP_DISABLED: "5015",
|
||||
MAGIC_LINK_LOGIN_DISABLED: "5016",
|
||||
PASSWORD_LOGIN_DISABLED: "5018",
|
||||
USER_ACCOUNT_DEACTIVATED: "5019",
|
||||
INSTANCE_NOT_CONFIGURED = "5000",
|
||||
INVALID_EMAIL = "5005",
|
||||
EMAIL_REQUIRED = "5010",
|
||||
SIGNUP_DISABLED = "5015",
|
||||
MAGIC_LINK_LOGIN_DISABLED = "5016",
|
||||
PASSWORD_LOGIN_DISABLED = "5018",
|
||||
USER_ACCOUNT_DEACTIVATED = "5019",
|
||||
// Password strength
|
||||
INVALID_PASSWORD: "5020",
|
||||
SMTP_NOT_CONFIGURED: "5025",
|
||||
INVALID_PASSWORD = "5020",
|
||||
SMTP_NOT_CONFIGURED = "5025",
|
||||
// Sign Up
|
||||
USER_ALREADY_EXIST: "5030",
|
||||
AUTHENTICATION_FAILED_SIGN_UP: "5035",
|
||||
REQUIRED_EMAIL_PASSWORD_SIGN_UP: "5040",
|
||||
INVALID_EMAIL_SIGN_UP: "5045",
|
||||
INVALID_EMAIL_MAGIC_SIGN_UP: "5050",
|
||||
MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED: "5055",
|
||||
USER_ALREADY_EXIST = "5030",
|
||||
AUTHENTICATION_FAILED_SIGN_UP = "5035",
|
||||
REQUIRED_EMAIL_PASSWORD_SIGN_UP = "5040",
|
||||
INVALID_EMAIL_SIGN_UP = "5045",
|
||||
INVALID_EMAIL_MAGIC_SIGN_UP = "5050",
|
||||
MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED = "5055",
|
||||
// Sign In
|
||||
USER_DOES_NOT_EXIST: "5060",
|
||||
AUTHENTICATION_FAILED_SIGN_IN: "5065",
|
||||
REQUIRED_EMAIL_PASSWORD_SIGN_IN: "5070",
|
||||
INVALID_EMAIL_SIGN_IN: "5075",
|
||||
INVALID_EMAIL_MAGIC_SIGN_IN: "5080",
|
||||
MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED: "5085",
|
||||
USER_DOES_NOT_EXIST = "5060",
|
||||
AUTHENTICATION_FAILED_SIGN_IN = "5065",
|
||||
REQUIRED_EMAIL_PASSWORD_SIGN_IN = "5070",
|
||||
INVALID_EMAIL_SIGN_IN = "5075",
|
||||
INVALID_EMAIL_MAGIC_SIGN_IN = "5080",
|
||||
MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED = "5085",
|
||||
// Both Sign in and Sign up for magic
|
||||
INVALID_MAGIC_CODE_SIGN_IN: "5090",
|
||||
INVALID_MAGIC_CODE_SIGN_UP: "5092",
|
||||
EXPIRED_MAGIC_CODE_SIGN_IN: "5095",
|
||||
EXPIRED_MAGIC_CODE_SIGN_UP: "5097",
|
||||
EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_IN: "5100",
|
||||
EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_UP: "5102",
|
||||
INVALID_MAGIC_CODE_SIGN_IN = "5090",
|
||||
INVALID_MAGIC_CODE_SIGN_UP = "5092",
|
||||
EXPIRED_MAGIC_CODE_SIGN_IN = "5095",
|
||||
EXPIRED_MAGIC_CODE_SIGN_UP = "5097",
|
||||
EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_IN = "5100",
|
||||
EMAIL_CODE_ATTEMPT_EXHAUSTED_SIGN_UP = "5102",
|
||||
// Oauth
|
||||
OAUTH_NOT_CONFIGURED: "5104",
|
||||
GOOGLE_NOT_CONFIGURED: "5105",
|
||||
GITHUB_NOT_CONFIGURED: "5110",
|
||||
GITLAB_NOT_CONFIGURED: "5111",
|
||||
GOOGLE_OAUTH_PROVIDER_ERROR: "5115",
|
||||
GITHUB_OAUTH_PROVIDER_ERROR: "5120",
|
||||
GITLAB_OAUTH_PROVIDER_ERROR: "5121",
|
||||
OAUTH_NOT_CONFIGURED = "5104",
|
||||
GOOGLE_NOT_CONFIGURED = "5105",
|
||||
GITHUB_NOT_CONFIGURED = "5110",
|
||||
GITLAB_NOT_CONFIGURED = "5111",
|
||||
GOOGLE_OAUTH_PROVIDER_ERROR = "5115",
|
||||
GITHUB_OAUTH_PROVIDER_ERROR = "5120",
|
||||
GITLAB_OAUTH_PROVIDER_ERROR = "5121",
|
||||
// Reset Password
|
||||
INVALID_PASSWORD_TOKEN: "5125",
|
||||
EXPIRED_PASSWORD_TOKEN: "5130",
|
||||
INVALID_PASSWORD_TOKEN = "5125",
|
||||
EXPIRED_PASSWORD_TOKEN = "5130",
|
||||
// Change password
|
||||
INCORRECT_OLD_PASSWORD: "5135",
|
||||
MISSING_PASSWORD: "5138",
|
||||
INVALID_NEW_PASSWORD: "5140",
|
||||
INCORRECT_OLD_PASSWORD = "5135",
|
||||
MISSING_PASSWORD = "5138",
|
||||
INVALID_NEW_PASSWORD = "5140",
|
||||
// set password
|
||||
PASSWORD_ALREADY_SET: "5145",
|
||||
PASSWORD_ALREADY_SET = "5145",
|
||||
// Admin
|
||||
ADMIN_ALREADY_EXIST: "5150",
|
||||
REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME: "5155",
|
||||
INVALID_ADMIN_EMAIL: "5160",
|
||||
INVALID_ADMIN_PASSWORD: "5165",
|
||||
REQUIRED_ADMIN_EMAIL_PASSWORD: "5170",
|
||||
ADMIN_AUTHENTICATION_FAILED: "5175",
|
||||
ADMIN_USER_ALREADY_EXIST: "5180",
|
||||
ADMIN_USER_DOES_NOT_EXIST: "5185",
|
||||
ADMIN_USER_DEACTIVATED: "5190",
|
||||
ADMIN_ALREADY_EXIST = "5150",
|
||||
REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME = "5155",
|
||||
INVALID_ADMIN_EMAIL = "5160",
|
||||
INVALID_ADMIN_PASSWORD = "5165",
|
||||
REQUIRED_ADMIN_EMAIL_PASSWORD = "5170",
|
||||
ADMIN_AUTHENTICATION_FAILED = "5175",
|
||||
ADMIN_USER_ALREADY_EXIST = "5180",
|
||||
ADMIN_USER_DOES_NOT_EXIST = "5185",
|
||||
ADMIN_USER_DEACTIVATED = "5190",
|
||||
// Rate limit
|
||||
RATE_LIMIT_EXCEEDED: "5900",
|
||||
} as const;
|
||||
|
||||
export type EAuthErrorCodes = typeof EAuthErrorCodes[keyof typeof EAuthErrorCodes];
|
||||
RATE_LIMIT_EXCEEDED = "5900",
|
||||
}
|
||||
|
||||
@@ -4,49 +4,43 @@ export const LABEL_CLASSNAME = "uppercase text-custom-text-300/60 text-sm tracki
|
||||
export const AXIS_LABEL_CLASSNAME = "uppercase text-custom-text-300/60 text-sm tracking-wide";
|
||||
|
||||
|
||||
export const ChartXAxisProperty = {
|
||||
STATES: "STATES",
|
||||
STATE_GROUPS: "STATE_GROUPS",
|
||||
LABELS: "LABELS",
|
||||
ASSIGNEES: "ASSIGNEES",
|
||||
ESTIMATE_POINTS: "ESTIMATE_POINTS",
|
||||
CYCLES: "CYCLES",
|
||||
MODULES: "MODULES",
|
||||
PRIORITY: "PRIORITY",
|
||||
START_DATE: "START_DATE",
|
||||
TARGET_DATE: "TARGET_DATE",
|
||||
CREATED_AT: "CREATED_AT",
|
||||
COMPLETED_AT: "COMPLETED_AT",
|
||||
CREATED_BY: "CREATED_BY",
|
||||
WORK_ITEM_TYPES: "WORK_ITEM_TYPES",
|
||||
PROJECTS: "PROJECTS",
|
||||
EPICS: "EPICS",
|
||||
} as const;
|
||||
export enum ChartXAxisProperty {
|
||||
STATES = "STATES",
|
||||
STATE_GROUPS = "STATE_GROUPS",
|
||||
LABELS = "LABELS",
|
||||
ASSIGNEES = "ASSIGNEES",
|
||||
ESTIMATE_POINTS = "ESTIMATE_POINTS",
|
||||
CYCLES = "CYCLES",
|
||||
MODULES = "MODULES",
|
||||
PRIORITY = "PRIORITY",
|
||||
START_DATE = "START_DATE",
|
||||
TARGET_DATE = "TARGET_DATE",
|
||||
CREATED_AT = "CREATED_AT",
|
||||
COMPLETED_AT = "COMPLETED_AT",
|
||||
CREATED_BY = "CREATED_BY",
|
||||
WORK_ITEM_TYPES = "WORK_ITEM_TYPES",
|
||||
PROJECTS = "PROJECTS",
|
||||
EPICS = "EPICS",
|
||||
}
|
||||
|
||||
export type ChartXAxisProperty = typeof ChartXAxisProperty[keyof typeof ChartXAxisProperty];
|
||||
|
||||
export const ChartYAxisMetric = {
|
||||
WORK_ITEM_COUNT: "WORK_ITEM_COUNT",
|
||||
ESTIMATE_POINT_COUNT: "ESTIMATE_POINT_COUNT",
|
||||
PENDING_WORK_ITEM_COUNT: "PENDING_WORK_ITEM_COUNT",
|
||||
COMPLETED_WORK_ITEM_COUNT: "COMPLETED_WORK_ITEM_COUNT",
|
||||
IN_PROGRESS_WORK_ITEM_COUNT: "IN_PROGRESS_WORK_ITEM_COUNT",
|
||||
WORK_ITEM_DUE_THIS_WEEK_COUNT: "WORK_ITEM_DUE_THIS_WEEK_COUNT",
|
||||
WORK_ITEM_DUE_TODAY_COUNT: "WORK_ITEM_DUE_TODAY_COUNT",
|
||||
BLOCKED_WORK_ITEM_COUNT: "BLOCKED_WORK_ITEM_COUNT",
|
||||
} as const;
|
||||
|
||||
export type ChartYAxisMetric = typeof ChartYAxisMetric[keyof typeof ChartYAxisMetric];
|
||||
export enum ChartYAxisMetric {
|
||||
WORK_ITEM_COUNT = "WORK_ITEM_COUNT",
|
||||
ESTIMATE_POINT_COUNT = "ESTIMATE_POINT_COUNT",
|
||||
PENDING_WORK_ITEM_COUNT = "PENDING_WORK_ITEM_COUNT",
|
||||
COMPLETED_WORK_ITEM_COUNT = "COMPLETED_WORK_ITEM_COUNT",
|
||||
IN_PROGRESS_WORK_ITEM_COUNT = "IN_PROGRESS_WORK_ITEM_COUNT",
|
||||
WORK_ITEM_DUE_THIS_WEEK_COUNT = "WORK_ITEM_DUE_THIS_WEEK_COUNT",
|
||||
WORK_ITEM_DUE_TODAY_COUNT = "WORK_ITEM_DUE_TODAY_COUNT",
|
||||
BLOCKED_WORK_ITEM_COUNT = "BLOCKED_WORK_ITEM_COUNT",
|
||||
}
|
||||
|
||||
|
||||
export const ChartXAxisDateGrouping = {
|
||||
DAY: "DAY",
|
||||
WEEK: "WEEK",
|
||||
MONTH: "MONTH",
|
||||
YEAR: "YEAR",
|
||||
} as const;
|
||||
|
||||
export type ChartXAxisDateGrouping = typeof ChartXAxisDateGrouping[keyof typeof ChartXAxisDateGrouping];
|
||||
export enum ChartXAxisDateGrouping {
|
||||
DAY = "DAY",
|
||||
WEEK = "WEEK",
|
||||
MONTH = "MONTH",
|
||||
YEAR = "YEAR",
|
||||
}
|
||||
|
||||
export const TO_CAPITALIZE_PROPERTIES: ChartXAxisProperty[] = [
|
||||
ChartXAxisProperty.PRIORITY,
|
||||
@@ -61,16 +55,14 @@ export const CHART_X_AXIS_DATE_PROPERTIES: ChartXAxisProperty[] = [
|
||||
];
|
||||
|
||||
|
||||
export const EChartModels = {
|
||||
BASIC: "BASIC",
|
||||
STACKED: "STACKED",
|
||||
GROUPED: "GROUPED",
|
||||
MULTI_LINE: "MULTI_LINE",
|
||||
COMPARISON: "COMPARISON",
|
||||
PROGRESS: "PROGRESS",
|
||||
} as const;
|
||||
|
||||
export type EChartModels = typeof EChartModels[keyof typeof EChartModels];
|
||||
export enum EChartModels {
|
||||
BASIC = "BASIC",
|
||||
STACKED = "STACKED",
|
||||
GROUPED = "GROUPED",
|
||||
MULTI_LINE = "MULTI_LINE",
|
||||
COMPARISON = "COMPARISON",
|
||||
PROGRESS = "PROGRESS",
|
||||
}
|
||||
|
||||
export const CHART_COLOR_PALETTES: {
|
||||
key: TChartColorScheme;
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
// types
|
||||
import { TIssuesListTypes } from "@plane/types";
|
||||
|
||||
export const EDurationFilters = {
|
||||
NONE: "none",
|
||||
TODAY: "today",
|
||||
THIS_WEEK: "this_week",
|
||||
THIS_MONTH: "this_month",
|
||||
THIS_YEAR: "this_year",
|
||||
CUSTOM: "custom",
|
||||
} as const;
|
||||
|
||||
export type EDurationFilters = typeof EDurationFilters[keyof typeof EDurationFilters];
|
||||
export enum EDurationFilters {
|
||||
NONE = "none",
|
||||
TODAY = "today",
|
||||
THIS_WEEK = "this_week",
|
||||
THIS_MONTH = "this_month",
|
||||
THIS_YEAR = "this_year",
|
||||
CUSTOM = "custom",
|
||||
}
|
||||
|
||||
// filter duration options
|
||||
export const DURATION_FILTER_OPTIONS: {
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
export const E_SORT_ORDER = {
|
||||
ASC: "asc",
|
||||
DESC: "desc",
|
||||
} as const;
|
||||
|
||||
export type E_SORT_ORDER = typeof E_SORT_ORDER[keyof typeof E_SORT_ORDER];
|
||||
export enum E_SORT_ORDER {
|
||||
ASC = "asc",
|
||||
DESC = "desc",
|
||||
}
|
||||
export const DATE_AFTER_FILTER_OPTIONS = [
|
||||
{
|
||||
name: "1 week from now",
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
export const EIconSize = {
|
||||
XS: "xs",
|
||||
SM: "sm",
|
||||
MD: "md",
|
||||
LG: "lg",
|
||||
XL: "xl",
|
||||
} as const;
|
||||
|
||||
export type EIconSize = typeof EIconSize[keyof typeof EIconSize];
|
||||
export enum EIconSize {
|
||||
XS = "xs",
|
||||
SM = "sm",
|
||||
MD = "md",
|
||||
LG = "lg",
|
||||
XL = "xl",
|
||||
}
|
||||
|
||||
@@ -1,29 +1,23 @@
|
||||
import { TInboxDuplicateIssueDetails, TIssue } from "@plane/types";
|
||||
|
||||
export const EInboxIssueCurrentTab = {
|
||||
OPEN: "open",
|
||||
CLOSED: "closed",
|
||||
} as const;
|
||||
export enum EInboxIssueCurrentTab {
|
||||
OPEN = "open",
|
||||
CLOSED = "closed",
|
||||
}
|
||||
|
||||
export type EInboxIssueCurrentTab = typeof EInboxIssueCurrentTab[keyof typeof EInboxIssueCurrentTab];
|
||||
export enum EInboxIssueStatus {
|
||||
PENDING = -2,
|
||||
DECLINED = -1,
|
||||
SNOOZED = 0,
|
||||
ACCEPTED = 1,
|
||||
DUPLICATE = 2,
|
||||
}
|
||||
|
||||
export const EInboxIssueStatus = {
|
||||
PENDING: -2,
|
||||
DECLINED: -1,
|
||||
SNOOZED: 0,
|
||||
ACCEPTED: 1,
|
||||
DUPLICATE: 2,
|
||||
} as const;
|
||||
|
||||
export type EInboxIssueStatus = typeof EInboxIssueStatus[keyof typeof EInboxIssueStatus];
|
||||
|
||||
export const EInboxIssueSource = {
|
||||
IN_APP: "IN_APP",
|
||||
FORMS: "FORMS",
|
||||
EMAIL: "EMAIL",
|
||||
} as const;
|
||||
|
||||
export type EInboxIssueSource = typeof EInboxIssueSource[keyof typeof EInboxIssueSource];
|
||||
export enum EInboxIssueSource {
|
||||
IN_APP = "IN_APP",
|
||||
FORMS = "FORMS",
|
||||
EMAIL = "EMAIL",
|
||||
}
|
||||
|
||||
export type TInboxIssueCurrentTab = EInboxIssueCurrentTab;
|
||||
export type TInboxIssueStatus = EInboxIssueStatus;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from "./ai";
|
||||
export * from "./analytics";
|
||||
export * from "./auth";
|
||||
export * from "./chart";
|
||||
export * from "./endpoints";
|
||||
@@ -33,4 +34,4 @@ export * from "./emoji";
|
||||
export * from "./subscription";
|
||||
export * from "./settings";
|
||||
export * from "./icon";
|
||||
export * from "./analytics";
|
||||
export * from "./analytics-v2";
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
export const EInstanceStatus = {
|
||||
ERROR: "ERROR",
|
||||
NOT_YET_READY: "NOT_YET_READY",
|
||||
} as const;
|
||||
|
||||
export type EInstanceStatus = typeof EInstanceStatus[keyof typeof EInstanceStatus];
|
||||
export enum EInstanceStatus {
|
||||
ERROR = "ERROR",
|
||||
NOT_YET_READY = "NOT_YET_READY",
|
||||
}
|
||||
|
||||
export type TInstanceStatus = {
|
||||
status: EInstanceStatus | undefined;
|
||||
|
||||
@@ -17,78 +17,66 @@ export type TIssueFilterPriorityObject = {
|
||||
icon: string;
|
||||
};
|
||||
|
||||
export const EIssueGroupByToServerOptions = {
|
||||
"state": "state_id",
|
||||
"priority": "priority",
|
||||
"labels": "labels__id",
|
||||
"state_detail.group": "state__group",
|
||||
"assignees": "assignees__id",
|
||||
"cycle": "cycle_id",
|
||||
"module": "issue_module__module_id",
|
||||
"target_date": "target_date",
|
||||
"project": "project_id",
|
||||
"created_by": "created_by",
|
||||
"team_project": "project_id",
|
||||
} as const;
|
||||
export enum EIssueGroupByToServerOptions {
|
||||
"state" = "state_id",
|
||||
"priority" = "priority",
|
||||
"labels" = "labels__id",
|
||||
"state_detail.group" = "state__group",
|
||||
"assignees" = "assignees__id",
|
||||
"cycle" = "cycle_id",
|
||||
"module" = "issue_module__module_id",
|
||||
"target_date" = "target_date",
|
||||
"project" = "project_id",
|
||||
"created_by" = "created_by",
|
||||
"team_project" = "project_id",
|
||||
}
|
||||
|
||||
export type EIssueGroupByToServerOptions = typeof EIssueGroupByToServerOptions[keyof typeof EIssueGroupByToServerOptions];
|
||||
export enum EIssueGroupBYServerToProperty {
|
||||
"state_id" = "state_id",
|
||||
"priority" = "priority",
|
||||
"labels__id" = "label_ids",
|
||||
"state__group" = "state__group",
|
||||
"assignees__id" = "assignee_ids",
|
||||
"cycle_id" = "cycle_id",
|
||||
"issue_module__module_id" = "module_ids",
|
||||
"target_date" = "target_date",
|
||||
"project_id" = "project_id",
|
||||
"created_by" = "created_by",
|
||||
}
|
||||
|
||||
export const EIssueGroupBYServerToProperty = {
|
||||
"state_id": "state_id",
|
||||
"priority": "priority",
|
||||
"labels__id": "label_ids",
|
||||
"state__group": "state__group",
|
||||
"assignees__id": "assignee_ids",
|
||||
"cycle_id": "cycle_id",
|
||||
"issue_module__module_id": "module_ids",
|
||||
"target_date": "target_date",
|
||||
"project_id": "project_id",
|
||||
"created_by": "created_by",
|
||||
} as const;
|
||||
export enum EIssueServiceType {
|
||||
ISSUES = "issues",
|
||||
EPICS = "epics",
|
||||
WORK_ITEMS = "work-items",
|
||||
}
|
||||
|
||||
export type EIssueGroupBYServerToProperty = typeof EIssueGroupBYServerToProperty[keyof typeof EIssueGroupBYServerToProperty];
|
||||
export enum EIssuesStoreType {
|
||||
GLOBAL = "GLOBAL",
|
||||
PROFILE = "PROFILE",
|
||||
TEAM = "TEAM",
|
||||
PROJECT = "PROJECT",
|
||||
CYCLE = "CYCLE",
|
||||
MODULE = "MODULE",
|
||||
TEAM_VIEW = "TEAM_VIEW",
|
||||
PROJECT_VIEW = "PROJECT_VIEW",
|
||||
ARCHIVED = "ARCHIVED",
|
||||
DRAFT = "DRAFT",
|
||||
DEFAULT = "DEFAULT",
|
||||
WORKSPACE_DRAFT = "WORKSPACE_DRAFT",
|
||||
EPIC = "EPIC",
|
||||
}
|
||||
|
||||
export const EIssueServiceType = {
|
||||
ISSUES: "issues",
|
||||
EPICS: "epics",
|
||||
WORK_ITEMS: "work-items",
|
||||
} as const;
|
||||
export enum EIssueCommentAccessSpecifier {
|
||||
EXTERNAL = "EXTERNAL",
|
||||
INTERNAL = "INTERNAL",
|
||||
}
|
||||
|
||||
export type EIssueServiceType = typeof EIssueServiceType[keyof typeof EIssueServiceType];
|
||||
|
||||
export const EIssuesStoreType = {
|
||||
GLOBAL: "GLOBAL",
|
||||
PROFILE: "PROFILE",
|
||||
TEAM: "TEAM",
|
||||
PROJECT: "PROJECT",
|
||||
CYCLE: "CYCLE",
|
||||
MODULE: "MODULE",
|
||||
TEAM_VIEW: "TEAM_VIEW",
|
||||
PROJECT_VIEW: "PROJECT_VIEW",
|
||||
ARCHIVED: "ARCHIVED",
|
||||
DRAFT: "DRAFT",
|
||||
DEFAULT: "DEFAULT",
|
||||
WORKSPACE_DRAFT: "WORKSPACE_DRAFT",
|
||||
EPIC: "EPIC",
|
||||
} as const;
|
||||
|
||||
export type EIssuesStoreType = typeof EIssuesStoreType[keyof typeof EIssuesStoreType];
|
||||
|
||||
export const EIssueCommentAccessSpecifier = {
|
||||
EXTERNAL: "EXTERNAL",
|
||||
INTERNAL: "INTERNAL",
|
||||
} as const;
|
||||
|
||||
export type EIssueCommentAccessSpecifier = typeof EIssueCommentAccessSpecifier[keyof typeof EIssueCommentAccessSpecifier];
|
||||
|
||||
export const EIssueListRow = {
|
||||
HEADER: "HEADER",
|
||||
ISSUE: "ISSUE",
|
||||
NO_ISSUES: "NO_ISSUES",
|
||||
QUICK_ADD: "QUICK_ADD",
|
||||
} as const;
|
||||
|
||||
export type EIssueListRow = typeof EIssueListRow[keyof typeof EIssueListRow];
|
||||
export enum EIssueListRow {
|
||||
HEADER = "HEADER",
|
||||
ISSUE = "ISSUE",
|
||||
NO_ISSUES = "NO_ISSUES",
|
||||
QUICK_ADD = "QUICK_ADD",
|
||||
}
|
||||
|
||||
export const ISSUE_PRIORITIES: {
|
||||
key: TIssuePriorities;
|
||||
@@ -126,14 +114,14 @@ export const DRAG_ALLOWED_GROUPS: TIssueGroupByOptions[] = [
|
||||
];
|
||||
|
||||
export type TCreateModalStoreTypes =
|
||||
| typeof EIssuesStoreType.TEAM
|
||||
| typeof EIssuesStoreType.PROJECT
|
||||
| typeof EIssuesStoreType.TEAM_VIEW
|
||||
| typeof EIssuesStoreType.PROJECT_VIEW
|
||||
| typeof EIssuesStoreType.PROFILE
|
||||
| typeof EIssuesStoreType.CYCLE
|
||||
| typeof EIssuesStoreType.MODULE
|
||||
| typeof EIssuesStoreType.EPIC;
|
||||
| EIssuesStoreType.TEAM
|
||||
| EIssuesStoreType.PROJECT
|
||||
| EIssuesStoreType.TEAM_VIEW
|
||||
| EIssuesStoreType.PROJECT_VIEW
|
||||
| EIssuesStoreType.PROFILE
|
||||
| EIssuesStoreType.CYCLE
|
||||
| EIssuesStoreType.MODULE
|
||||
| EIssuesStoreType.EPIC;
|
||||
|
||||
export const ISSUE_GROUP_BY_OPTIONS: {
|
||||
key: TIssueGroupByOptions;
|
||||
|
||||
@@ -10,29 +10,25 @@ import { TIssueLayout } from "./layout";
|
||||
|
||||
export type TIssueFilterKeys = "priority" | "state" | "labels";
|
||||
|
||||
export const EServerGroupByToFilterOptions = {
|
||||
"state_id": "state",
|
||||
"priority": "priority",
|
||||
"labels__id": "labels",
|
||||
"state__group": "state_group",
|
||||
"assignees__id": "assignees",
|
||||
"cycle_id": "cycle",
|
||||
"issue_module__module_id": "module",
|
||||
"target_date": "target_date",
|
||||
"project_id": "project",
|
||||
"created_by": "created_by",
|
||||
} as const;
|
||||
export enum EServerGroupByToFilterOptions {
|
||||
"state_id" = "state",
|
||||
"priority" = "priority",
|
||||
"labels__id" = "labels",
|
||||
"state__group" = "state_group",
|
||||
"assignees__id" = "assignees",
|
||||
"cycle_id" = "cycle",
|
||||
"issue_module__module_id" = "module",
|
||||
"target_date" = "target_date",
|
||||
"project_id" = "project",
|
||||
"created_by" = "created_by",
|
||||
}
|
||||
|
||||
export type EServerGroupByToFilterOptions = typeof EServerGroupByToFilterOptions[keyof typeof EServerGroupByToFilterOptions];
|
||||
|
||||
export const EIssueFilterType = {
|
||||
FILTERS: "filters",
|
||||
DISPLAY_FILTERS: "display_filters",
|
||||
DISPLAY_PROPERTIES: "display_properties",
|
||||
KANBAN_FILTERS: "kanban_filters",
|
||||
} as const;
|
||||
|
||||
export type EIssueFilterType = typeof EIssueFilterType[keyof typeof EIssueFilterType];
|
||||
export enum EIssueFilterType {
|
||||
FILTERS = "filters",
|
||||
DISPLAY_FILTERS = "display_filters",
|
||||
DISPLAY_PROPERTIES = "display_properties",
|
||||
KANBAN_FILTERS = "kanban_filters",
|
||||
}
|
||||
|
||||
export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: {
|
||||
[key in TIssueLayout]: Record<"filters", TIssueFilterKeys[]>;
|
||||
@@ -140,7 +136,45 @@ export const ISSUE_DISPLAY_FILTERS_BY_PAGE: TIssueFiltersToDisplayByPageType = {
|
||||
],
|
||||
display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS,
|
||||
display_filters: {
|
||||
group_by: ["state", "cycle", "module", "priority", "labels", "assignees", "created_by", null],
|
||||
group_by: [
|
||||
"state",
|
||||
"cycle",
|
||||
"module",
|
||||
"state_detail.group",
|
||||
"priority",
|
||||
"labels",
|
||||
"assignees",
|
||||
"created_by",
|
||||
null,
|
||||
],
|
||||
order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"],
|
||||
type: [null, "active", "backlog"],
|
||||
},
|
||||
extra_options: {
|
||||
access: true,
|
||||
values: ["show_empty_groups"],
|
||||
},
|
||||
},
|
||||
},
|
||||
draft_issues: {
|
||||
list: {
|
||||
filters: ["priority", "state_group", "cycle", "module", "labels", "start_date", "target_date", "issue_type"],
|
||||
display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS,
|
||||
display_filters: {
|
||||
group_by: ["state_detail.group", "cycle", "module", "priority", "project", "labels", null],
|
||||
order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"],
|
||||
type: [null, "active", "backlog"],
|
||||
},
|
||||
extra_options: {
|
||||
access: true,
|
||||
values: ["show_empty_groups"],
|
||||
},
|
||||
},
|
||||
kanban: {
|
||||
filters: ["priority", "state_group", "cycle", "module", "labels", "start_date", "target_date", "issue_type"],
|
||||
display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS,
|
||||
display_filters: {
|
||||
group_by: ["state_detail.group", "cycle", "module", "priority", "project", "labels"],
|
||||
order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"],
|
||||
type: [null, "active", "backlog"],
|
||||
},
|
||||
@@ -338,12 +372,10 @@ export const ISSUE_STORE_TO_FILTERS_MAP: Partial<Record<EIssuesStoreType, TFilte
|
||||
[EIssuesStoreType.PROJECT]: ISSUE_DISPLAY_FILTERS_BY_PAGE.issues,
|
||||
};
|
||||
|
||||
export const EActivityFilterType = {
|
||||
ACTIVITY: "ACTIVITY",
|
||||
COMMENT: "COMMENT",
|
||||
} as const;
|
||||
|
||||
export type EActivityFilterType = typeof EActivityFilterType[keyof typeof EActivityFilterType];
|
||||
export enum EActivityFilterType {
|
||||
ACTIVITY = "ACTIVITY",
|
||||
COMMENT = "COMMENT",
|
||||
}
|
||||
|
||||
export type TActivityFilters = EActivityFilterType;
|
||||
|
||||
|
||||
@@ -5,15 +5,13 @@ export type TIssueLayout =
|
||||
| "spreadsheet"
|
||||
| "gantt";
|
||||
|
||||
export const EIssueLayoutTypes = {
|
||||
LIST: "list",
|
||||
KANBAN: "kanban",
|
||||
CALENDAR: "calendar",
|
||||
GANTT: "gantt_chart",
|
||||
SPREADSHEET: "spreadsheet",
|
||||
} as const;
|
||||
|
||||
export type EIssueLayoutTypes = typeof EIssueLayoutTypes[keyof typeof EIssueLayoutTypes];
|
||||
export enum EIssueLayoutTypes {
|
||||
LIST = "list",
|
||||
KANBAN = "kanban",
|
||||
CALENDAR = "calendar",
|
||||
GANTT = "gantt_chart",
|
||||
SPREADSHEET = "spreadsheet",
|
||||
}
|
||||
|
||||
export type TIssueLayoutMap = Record<
|
||||
EIssueLayoutTypes,
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
// types
|
||||
import { TModuleLayoutOptions, TModuleOrderByOptions, TModuleStatus } from "@plane/types";
|
||||
|
||||
export const MODULE_STATUS_COLORS: {
|
||||
[key in TModuleStatus]: string;
|
||||
} = {
|
||||
backlog: "#a3a3a2",
|
||||
planned: "#3f76ff",
|
||||
paused: "#525252",
|
||||
completed: "#16a34a",
|
||||
cancelled: "#ef4444",
|
||||
"in-progress": "#f39e1f",
|
||||
};
|
||||
import {
|
||||
TModuleLayoutOptions,
|
||||
TModuleOrderByOptions,
|
||||
TModuleStatus,
|
||||
} from "@plane/types";
|
||||
|
||||
export const MODULE_STATUS: {
|
||||
i18n_label: string;
|
||||
@@ -22,42 +15,42 @@ export const MODULE_STATUS: {
|
||||
{
|
||||
i18n_label: "project_modules.status.backlog",
|
||||
value: "backlog",
|
||||
color: MODULE_STATUS_COLORS.backlog,
|
||||
color: "#a3a3a2",
|
||||
textColor: "text-custom-text-400",
|
||||
bgColor: "bg-custom-background-80",
|
||||
},
|
||||
{
|
||||
i18n_label: "project_modules.status.planned",
|
||||
value: "planned",
|
||||
color: MODULE_STATUS_COLORS.planned,
|
||||
color: "#3f76ff",
|
||||
textColor: "text-blue-500",
|
||||
bgColor: "bg-indigo-50",
|
||||
},
|
||||
{
|
||||
i18n_label: "project_modules.status.in_progress",
|
||||
value: "in-progress",
|
||||
color: MODULE_STATUS_COLORS["in-progress"],
|
||||
color: "#f39e1f",
|
||||
textColor: "text-amber-500",
|
||||
bgColor: "bg-amber-50",
|
||||
},
|
||||
{
|
||||
i18n_label: "project_modules.status.paused",
|
||||
value: "paused",
|
||||
color: MODULE_STATUS_COLORS.paused,
|
||||
color: "#525252",
|
||||
textColor: "text-custom-text-300",
|
||||
bgColor: "bg-custom-background-90",
|
||||
},
|
||||
{
|
||||
i18n_label: "project_modules.status.completed",
|
||||
value: "completed",
|
||||
color: MODULE_STATUS_COLORS.completed,
|
||||
color: "#16a34a",
|
||||
textColor: "text-green-600",
|
||||
bgColor: "bg-green-100",
|
||||
},
|
||||
{
|
||||
i18n_label: "project_modules.status.cancelled",
|
||||
value: "cancelled",
|
||||
color: MODULE_STATUS_COLORS.cancelled,
|
||||
color: "#ef4444",
|
||||
textColor: "text-red-500",
|
||||
bgColor: "bg-red-50",
|
||||
},
|
||||
|
||||
@@ -1,39 +1,31 @@
|
||||
import { TUnreadNotificationsCount } from "@plane/types";
|
||||
|
||||
export const ENotificationTab = {
|
||||
ALL: "all",
|
||||
MENTIONS: "mentions",
|
||||
} as const;
|
||||
export enum ENotificationTab {
|
||||
ALL = "all",
|
||||
MENTIONS = "mentions",
|
||||
}
|
||||
|
||||
export type ENotificationTab = typeof ENotificationTab[keyof typeof ENotificationTab];
|
||||
export enum ENotificationFilterType {
|
||||
CREATED = "created",
|
||||
ASSIGNED = "assigned",
|
||||
SUBSCRIBED = "subscribed",
|
||||
}
|
||||
|
||||
export const ENotificationFilterType = {
|
||||
CREATED: "created",
|
||||
ASSIGNED: "assigned",
|
||||
SUBSCRIBED: "subscribed",
|
||||
} as const;
|
||||
export enum ENotificationLoader {
|
||||
INIT_LOADER = "init-loader",
|
||||
MUTATION_LOADER = "mutation-loader",
|
||||
PAGINATION_LOADER = "pagination-loader",
|
||||
REFRESH = "refresh",
|
||||
MARK_ALL_AS_READY = "mark-all-as-read",
|
||||
}
|
||||
|
||||
export type ENotificationFilterType = typeof ENotificationFilterType[keyof typeof ENotificationFilterType];
|
||||
export enum ENotificationQueryParamType {
|
||||
INIT = "init",
|
||||
CURRENT = "current",
|
||||
NEXT = "next",
|
||||
}
|
||||
|
||||
export const ENotificationLoader = {
|
||||
INIT_LOADER: "init-loader",
|
||||
MUTATION_LOADER: "mutation-loader",
|
||||
PAGINATION_LOADER: "pagination-loader",
|
||||
REFRESH: "refresh",
|
||||
MARK_ALL_AS_READY: "mark-all-as-read",
|
||||
} as const;
|
||||
|
||||
export type ENotificationLoader = typeof ENotificationLoader[keyof typeof ENotificationLoader];
|
||||
|
||||
export const ENotificationQueryParamType = {
|
||||
INIT: "init",
|
||||
CURRENT: "current",
|
||||
NEXT: "next",
|
||||
} as const;
|
||||
|
||||
export type ENotificationQueryParamType = typeof ENotificationQueryParamType[keyof typeof ENotificationQueryParamType];
|
||||
|
||||
export type TNotificationTab = typeof ENotificationTab.ALL | typeof ENotificationTab.MENTIONS;
|
||||
export type TNotificationTab = ENotificationTab.ALL | ENotificationTab.MENTIONS;
|
||||
|
||||
export const NOTIFICATION_TABS = [
|
||||
{
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
export const EPageAccess = {
|
||||
PUBLIC: 0,
|
||||
PRIVATE: 1,
|
||||
} as const;
|
||||
|
||||
export type EPageAccess = typeof EPageAccess[keyof typeof EPageAccess];
|
||||
export enum EPageAccess {
|
||||
PUBLIC = 0,
|
||||
PRIVATE = 1,
|
||||
}
|
||||
|
||||
export type TCreatePageModal = {
|
||||
isOpen: boolean;
|
||||
|
||||
@@ -3,15 +3,13 @@ import { IPaymentProduct, TBillingFrequency, TProductBillingFrequency } from "@p
|
||||
/**
|
||||
* Enum representing different product subscription types
|
||||
*/
|
||||
export const EProductSubscriptionEnum = {
|
||||
FREE: "FREE",
|
||||
ONE: "ONE",
|
||||
PRO: "PRO",
|
||||
BUSINESS: "BUSINESS",
|
||||
ENTERPRISE: "ENTERPRISE",
|
||||
} as const;
|
||||
|
||||
export type EProductSubscriptionEnum = typeof EProductSubscriptionEnum[keyof typeof EProductSubscriptionEnum];
|
||||
export enum EProductSubscriptionEnum {
|
||||
FREE = "FREE",
|
||||
ONE = "ONE",
|
||||
PRO = "PRO",
|
||||
BUSINESS = "BUSINESS",
|
||||
ENTERPRISE = "ENTERPRISE",
|
||||
}
|
||||
|
||||
/**
|
||||
* Default billing frequency for each product subscription type
|
||||
@@ -31,7 +29,7 @@ export const SUBSCRIPTION_WITH_BILLING_FREQUENCY = [
|
||||
EProductSubscriptionEnum.PRO,
|
||||
EProductSubscriptionEnum.BUSINESS,
|
||||
EProductSubscriptionEnum.ENTERPRISE,
|
||||
] as EProductSubscriptionEnum[];
|
||||
];
|
||||
|
||||
/**
|
||||
* Mapping of product subscription types to their respective payment product details
|
||||
@@ -74,23 +72,23 @@ export const PLANE_COMMUNITY_PRODUCTS: Record<string, IPaymentProduct> = {
|
||||
prices: [
|
||||
{
|
||||
id: `price_yearly_${EProductSubscriptionEnum.BUSINESS}`,
|
||||
unit_amount: 15600,
|
||||
unit_amount: 0,
|
||||
recurring: "year",
|
||||
currency: "usd",
|
||||
workspace_amount: 15600,
|
||||
workspace_amount: 0,
|
||||
product: EProductSubscriptionEnum.BUSINESS,
|
||||
},
|
||||
{
|
||||
id: `price_monthly_${EProductSubscriptionEnum.BUSINESS}`,
|
||||
unit_amount: 1500,
|
||||
unit_amount: 0,
|
||||
recurring: "month",
|
||||
currency: "usd",
|
||||
workspace_amount: 1500,
|
||||
workspace_amount: 0,
|
||||
product: EProductSubscriptionEnum.BUSINESS,
|
||||
},
|
||||
],
|
||||
payment_quantity: 1,
|
||||
is_active: true,
|
||||
is_active: false,
|
||||
},
|
||||
[EProductSubscriptionEnum.ENTERPRISE]: {
|
||||
id: EProductSubscriptionEnum.ENTERPRISE,
|
||||
@@ -143,8 +141,8 @@ export const SUBSCRIPTION_REDIRECTION_URLS: Record<EProductSubscriptionEnum, Rec
|
||||
year: "https://app.plane.so/upgrade/pro/self-hosted?plan=year",
|
||||
},
|
||||
[EProductSubscriptionEnum.BUSINESS]: {
|
||||
month: "https://app.plane.so/upgrade/business/self-hosted?plan=month",
|
||||
year: "https://app.plane.so/upgrade/business/self-hosted?plan=year",
|
||||
month: TALK_TO_SALES_URL,
|
||||
year: TALK_TO_SALES_URL,
|
||||
},
|
||||
[EProductSubscriptionEnum.ENTERPRISE]: {
|
||||
month: TALK_TO_SALES_URL,
|
||||
|
||||
@@ -107,17 +107,15 @@ export const PREFERENCE_OPTIONS: {
|
||||
* @description The start of the week for the user
|
||||
* @enum {number}
|
||||
*/
|
||||
export const EStartOfTheWeek = {
|
||||
SUNDAY: 0,
|
||||
MONDAY: 1,
|
||||
TUESDAY: 2,
|
||||
WEDNESDAY: 3,
|
||||
THURSDAY: 4,
|
||||
FRIDAY: 5,
|
||||
SATURDAY: 6,
|
||||
} as const;
|
||||
|
||||
export type EStartOfTheWeek = typeof EStartOfTheWeek[keyof typeof EStartOfTheWeek];
|
||||
export enum EStartOfTheWeek {
|
||||
SUNDAY = 0,
|
||||
MONDAY = 1,
|
||||
TUESDAY = 2,
|
||||
WEDNESDAY = 3,
|
||||
THURSDAY = 4,
|
||||
FRIDAY = 5,
|
||||
SATURDAY = 6,
|
||||
}
|
||||
|
||||
/**
|
||||
* @description The options for the start of the week
|
||||
|
||||
@@ -1,26 +1,20 @@
|
||||
import { PROFILE_SETTINGS } from ".";
|
||||
import { WORKSPACE_SETTINGS } from "./workspace";
|
||||
|
||||
export const WORKSPACE_SETTINGS_CATEGORY = {
|
||||
ADMINISTRATION: "administration",
|
||||
FEATURES: "features",
|
||||
DEVELOPER: "developer",
|
||||
} as const;
|
||||
export enum WORKSPACE_SETTINGS_CATEGORY {
|
||||
ADMINISTRATION = "administration",
|
||||
FEATURES = "features",
|
||||
DEVELOPER = "developer",
|
||||
}
|
||||
|
||||
export type WORKSPACE_SETTINGS_CATEGORY = typeof WORKSPACE_SETTINGS_CATEGORY[keyof typeof WORKSPACE_SETTINGS_CATEGORY];
|
||||
export enum PROFILE_SETTINGS_CATEGORY {
|
||||
YOUR_PROFILE = "your profile",
|
||||
DEVELOPER = "developer",
|
||||
}
|
||||
|
||||
export const PROFILE_SETTINGS_CATEGORY = {
|
||||
YOUR_PROFILE: "your profile",
|
||||
DEVELOPER: "developer",
|
||||
} as const;
|
||||
|
||||
export type PROFILE_SETTINGS_CATEGORY = typeof PROFILE_SETTINGS_CATEGORY[keyof typeof PROFILE_SETTINGS_CATEGORY];
|
||||
|
||||
export const PROJECT_SETTINGS_CATEGORY = {
|
||||
PROJECTS: "projects",
|
||||
} as const;
|
||||
|
||||
export type PROJECT_SETTINGS_CATEGORY = typeof PROJECT_SETTINGS_CATEGORY[keyof typeof PROJECT_SETTINGS_CATEGORY];
|
||||
export enum PROJECT_SETTINGS_CATEGORY {
|
||||
PROJECTS = "projects",
|
||||
}
|
||||
|
||||
export const WORKSPACE_SETTINGS_CATEGORIES = [
|
||||
WORKSPACE_SETTINGS_CATEGORY.ADMINISTRATION,
|
||||
|
||||
@@ -89,18 +89,16 @@ export const PROJECT_PAGE_TAB_INDICES = [
|
||||
"submit",
|
||||
];
|
||||
|
||||
export const ETabIndices = {
|
||||
ISSUE_FORM: "issue-form",
|
||||
INTAKE_ISSUE_FORM: "intake-issue-form",
|
||||
CREATE_LABEL: "create-label",
|
||||
PROJECT_CREATE: "project-create",
|
||||
PROJECT_CYCLE: "project-cycle",
|
||||
PROJECT_MODULE: "project-module",
|
||||
PROJECT_VIEW: "project-view",
|
||||
PROJECT_PAGE: "project-page",
|
||||
} as const;
|
||||
|
||||
export type ETabIndices = typeof ETabIndices[keyof typeof ETabIndices];
|
||||
export enum ETabIndices {
|
||||
ISSUE_FORM = "issue-form",
|
||||
INTAKE_ISSUE_FORM = "intake-issue-form",
|
||||
CREATE_LABEL = "create-label",
|
||||
PROJECT_CREATE = "project-create",
|
||||
PROJECT_CYCLE = "project-cycle",
|
||||
PROJECT_MODULE = "project-module",
|
||||
PROJECT_VIEW = "project-view",
|
||||
PROJECT_PAGE = "project-page",
|
||||
}
|
||||
|
||||
export const TAB_INDEX_MAP: Record<ETabIndices, string[]> = {
|
||||
[ETabIndices.ISSUE_FORM]: ISSUE_FORM_TAB_INDICES,
|
||||
|
||||
@@ -1,63 +1,49 @@
|
||||
export const EAuthenticationPageType = {
|
||||
STATIC: "STATIC",
|
||||
NOT_AUTHENTICATED: "NOT_AUTHENTICATED",
|
||||
AUTHENTICATED: "AUTHENTICATED",
|
||||
} as const;
|
||||
export enum EAuthenticationPageType {
|
||||
STATIC = "STATIC",
|
||||
NOT_AUTHENTICATED = "NOT_AUTHENTICATED",
|
||||
AUTHENTICATED = "AUTHENTICATED",
|
||||
}
|
||||
|
||||
export type EAuthenticationPageType = typeof EAuthenticationPageType[keyof typeof EAuthenticationPageType];
|
||||
export enum EInstancePageType {
|
||||
PRE_SETUP = "PRE_SETUP",
|
||||
POST_SETUP = "POST_SETUP",
|
||||
}
|
||||
|
||||
export const EInstancePageType = {
|
||||
PRE_SETUP: "PRE_SETUP",
|
||||
POST_SETUP: "POST_SETUP",
|
||||
} as const;
|
||||
|
||||
export type EInstancePageType = typeof EInstancePageType[keyof typeof EInstancePageType];
|
||||
|
||||
export const EUserStatus = {
|
||||
ERROR: "ERROR",
|
||||
AUTHENTICATION_NOT_DONE: "AUTHENTICATION_NOT_DONE",
|
||||
NOT_YET_READY: "NOT_YET_READY",
|
||||
} as const;
|
||||
|
||||
export type EUserStatus = typeof EUserStatus[keyof typeof EUserStatus];
|
||||
export enum EUserStatus {
|
||||
ERROR = "ERROR",
|
||||
AUTHENTICATION_NOT_DONE = "AUTHENTICATION_NOT_DONE",
|
||||
NOT_YET_READY = "NOT_YET_READY",
|
||||
}
|
||||
|
||||
export type TUserStatus = {
|
||||
status: EUserStatus | undefined;
|
||||
message?: string;
|
||||
};
|
||||
|
||||
export const EUserPermissionsLevel = {
|
||||
WORKSPACE: "WORKSPACE",
|
||||
PROJECT: "PROJECT",
|
||||
} as const;
|
||||
export enum EUserPermissionsLevel {
|
||||
WORKSPACE = "WORKSPACE",
|
||||
PROJECT = "PROJECT",
|
||||
}
|
||||
|
||||
export type EUserPermissionsLevel = typeof EUserPermissionsLevel[keyof typeof EUserPermissionsLevel];
|
||||
export enum EUserWorkspaceRoles {
|
||||
ADMIN = 20,
|
||||
MEMBER = 15,
|
||||
GUEST = 5,
|
||||
}
|
||||
|
||||
export const EUserWorkspaceRoles = {
|
||||
ADMIN: 20,
|
||||
MEMBER: 15,
|
||||
GUEST: 5,
|
||||
} as const;
|
||||
|
||||
export type EUserWorkspaceRoles = typeof EUserWorkspaceRoles[keyof typeof EUserWorkspaceRoles];
|
||||
|
||||
export const EUserProjectRoles = {
|
||||
ADMIN: 20,
|
||||
MEMBER: 15,
|
||||
GUEST: 5,
|
||||
} as const;
|
||||
|
||||
export type EUserProjectRoles = typeof EUserProjectRoles[keyof typeof EUserProjectRoles];
|
||||
export enum EUserProjectRoles {
|
||||
ADMIN = 20,
|
||||
MEMBER = 15,
|
||||
GUEST = 5,
|
||||
}
|
||||
|
||||
export type TUserPermissionsLevel = EUserPermissionsLevel;
|
||||
|
||||
export const EUserPermissions = {
|
||||
ADMIN: 20,
|
||||
MEMBER: 15,
|
||||
GUEST: 5,
|
||||
} as const;
|
||||
|
||||
export type EUserPermissions = typeof EUserPermissions[keyof typeof EUserPermissions];
|
||||
export enum EUserPermissions {
|
||||
ADMIN = 20,
|
||||
MEMBER = 15,
|
||||
GUEST = 5,
|
||||
}
|
||||
export type TUserPermissions = EUserPermissions;
|
||||
|
||||
export type TUserAllowedPermissionsObject = {
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
export const EViewAccess = {
|
||||
PRIVATE: 0,
|
||||
PUBLIC: 1,
|
||||
} as const;
|
||||
|
||||
export type EViewAccess = typeof EViewAccess[keyof typeof EViewAccess];
|
||||
export enum EViewAccess {
|
||||
PRIVATE,
|
||||
PUBLIC,
|
||||
}
|
||||
|
||||
export const VIEW_ACCESS_SPECIFIERS: {
|
||||
key: EViewAccess;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
export const EDraftIssuePaginationType = {
|
||||
INIT: "INIT",
|
||||
NEXT: "NEXT",
|
||||
PREV: "PREV",
|
||||
CURRENT: "CURRENT",
|
||||
} as const;
|
||||
|
||||
export type EDraftIssuePaginationType = typeof EDraftIssuePaginationType[keyof typeof EDraftIssuePaginationType];
|
||||
export enum EDraftIssuePaginationType {
|
||||
INIT = "INIT",
|
||||
NEXT = "NEXT",
|
||||
PREV = "PREV",
|
||||
CURRENT = "CURRENT",
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ export const WORKSPACE_SETTINGS = {
|
||||
|
||||
export const WORKSPACE_SETTINGS_ACCESS = Object.fromEntries(
|
||||
Object.entries(WORKSPACE_SETTINGS).map(([_, { href, access }]) => [href, access])
|
||||
) as Record<string, EUserWorkspaceRoles[]>;
|
||||
);
|
||||
|
||||
export const WORKSPACE_SETTINGS_LINKS: {
|
||||
key: string;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"@types/reflect-metadata": "^0.1.0",
|
||||
"@types/ws": "^8.5.10",
|
||||
"tsup": "8.4.0",
|
||||
"typescript": "5.8.3"
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"express": ">=4.21.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@plane/editor",
|
||||
"version": "0.26.1",
|
||||
"version": "0.26.0",
|
||||
"description": "Core Editor that powers Plane",
|
||||
"license": "AGPL-3.0",
|
||||
"private": true,
|
||||
@@ -82,7 +82,7 @@
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"postcss": "^8.4.38",
|
||||
"tsup": "8.4.0",
|
||||
"typescript": "5.8.3"
|
||||
"typescript": "5.3.3"
|
||||
},
|
||||
"keywords": [
|
||||
"editor",
|
||||
|
||||
@@ -2,31 +2,30 @@ import { HocuspocusProvider } from "@hocuspocus/provider";
|
||||
import { AnyExtension } from "@tiptap/core";
|
||||
import { SlashCommands } from "@/extensions";
|
||||
// plane editor types
|
||||
import { TEmbedConfig } from "@/plane-editor/types";
|
||||
import { TIssueEmbedConfig } from "@/plane-editor/types";
|
||||
// types
|
||||
import { TExtensions, TFileHandler, TUserDetails } from "@/types";
|
||||
import { TExtensions, TUserDetails } from "@/types";
|
||||
|
||||
export type TDocumentEditorAdditionalExtensionsProps = {
|
||||
disabledExtensions: TExtensions[];
|
||||
embedConfig: TEmbedConfig | undefined;
|
||||
fileHandler: TFileHandler;
|
||||
provider?: HocuspocusProvider;
|
||||
type Props = {
|
||||
disabledExtensions?: TExtensions[];
|
||||
issueEmbedConfig: TIssueEmbedConfig | undefined;
|
||||
provider: HocuspocusProvider;
|
||||
userDetails: TUserDetails;
|
||||
};
|
||||
|
||||
export type TDocumentEditorAdditionalExtensionsRegistry = {
|
||||
type ExtensionConfig = {
|
||||
isEnabled: (disabledExtensions: TExtensions[]) => boolean;
|
||||
getExtension: (props: TDocumentEditorAdditionalExtensionsProps) => AnyExtension;
|
||||
getExtension: (props: Props) => AnyExtension;
|
||||
};
|
||||
|
||||
const extensionRegistry: TDocumentEditorAdditionalExtensionsRegistry[] = [
|
||||
const extensionRegistry: ExtensionConfig[] = [
|
||||
{
|
||||
isEnabled: (disabledExtensions) => !disabledExtensions.includes("slash-commands"),
|
||||
getExtension: ({ disabledExtensions }) => SlashCommands({ disabledExtensions }),
|
||||
getExtension: () => SlashCommands({}),
|
||||
},
|
||||
];
|
||||
|
||||
export const DocumentEditorAdditionalExtensions = (_props: TDocumentEditorAdditionalExtensionsProps) => {
|
||||
export const DocumentEditorAdditionalExtensions = (_props: Props) => {
|
||||
const { disabledExtensions = [] } = _props;
|
||||
|
||||
const documentExtensions = extensionRegistry
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import { AnyExtension, Extensions } from "@tiptap/core";
|
||||
// extensions
|
||||
import { SlashCommands } from "@/extensions/slash-commands/root";
|
||||
// types
|
||||
import { TExtensions, TFileHandler } from "@/types";
|
||||
|
||||
export type TRichTextEditorAdditionalExtensionsProps = {
|
||||
disabledExtensions: TExtensions[];
|
||||
fileHandler: TFileHandler;
|
||||
};
|
||||
|
||||
/**
|
||||
* Registry entry configuration for extensions
|
||||
*/
|
||||
export type TRichTextEditorAdditionalExtensionsRegistry = {
|
||||
/** Determines if the extension should be enabled based on disabled extensions */
|
||||
isEnabled: (disabledExtensions: TExtensions[]) => boolean;
|
||||
/** Returns the extension instance(s) when enabled */
|
||||
getExtension: (props: TRichTextEditorAdditionalExtensionsProps) => AnyExtension | undefined;
|
||||
};
|
||||
|
||||
const extensionRegistry: TRichTextEditorAdditionalExtensionsRegistry[] = [
|
||||
{
|
||||
isEnabled: (disabledExtensions) => !disabledExtensions.includes("slash-commands"),
|
||||
getExtension: ({ disabledExtensions }) =>
|
||||
SlashCommands({
|
||||
disabledExtensions,
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
export const RichTextEditorAdditionalExtensions = (props: TRichTextEditorAdditionalExtensionsProps) => {
|
||||
const { disabledExtensions } = props;
|
||||
|
||||
const extensions: Extensions = extensionRegistry
|
||||
.filter((config) => config.isEnabled(disabledExtensions))
|
||||
.map((config) => config.getExtension(props))
|
||||
.filter((extension): extension is AnyExtension => extension !== undefined);
|
||||
|
||||
return extensions;
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
import { AnyExtension, Extensions } from "@tiptap/core";
|
||||
// types
|
||||
import { TExtensions, TReadOnlyFileHandler } from "@/types";
|
||||
|
||||
export type TRichTextReadOnlyEditorAdditionalExtensionsProps = {
|
||||
disabledExtensions: TExtensions[];
|
||||
fileHandler: TReadOnlyFileHandler;
|
||||
};
|
||||
|
||||
/**
|
||||
* Registry entry configuration for extensions
|
||||
*/
|
||||
export type TRichTextReadOnlyEditorAdditionalExtensionsRegistry = {
|
||||
/** Determines if the extension should be enabled based on disabled extensions */
|
||||
isEnabled: (disabledExtensions: TExtensions[]) => boolean;
|
||||
/** Returns the extension instance(s) when enabled */
|
||||
getExtension: (props: TRichTextReadOnlyEditorAdditionalExtensionsProps) => AnyExtension | undefined;
|
||||
};
|
||||
|
||||
const extensionRegistry: TRichTextReadOnlyEditorAdditionalExtensionsRegistry[] = [];
|
||||
|
||||
export const RichTextReadOnlyEditorAdditionalExtensions = (props: TRichTextReadOnlyEditorAdditionalExtensionsProps) => {
|
||||
const { disabledExtensions } = props;
|
||||
|
||||
const extensions: Extensions = extensionRegistry
|
||||
.filter((config) => config.isEnabled(disabledExtensions))
|
||||
.map((config) => config.getExtension(props))
|
||||
.filter((extension): extension is AnyExtension => extension !== undefined);
|
||||
|
||||
return extensions;
|
||||
};
|
||||
@@ -15,7 +15,6 @@ export const ReadOnlyEditorWrapper = (props: IReadOnlyEditorProps) => {
|
||||
disabledExtensions,
|
||||
displayConfig = DEFAULT_DISPLAY_CONFIG,
|
||||
editorClassName = "",
|
||||
extensions,
|
||||
fileHandler,
|
||||
forwardedRef,
|
||||
id,
|
||||
@@ -26,7 +25,6 @@ export const ReadOnlyEditorWrapper = (props: IReadOnlyEditorProps) => {
|
||||
const editor = useReadOnlyEditor({
|
||||
disabledExtensions,
|
||||
editorClassName,
|
||||
extensions,
|
||||
fileHandler,
|
||||
forwardedRef,
|
||||
initialValue,
|
||||
|
||||
@@ -3,20 +3,12 @@ import { forwardRef, useCallback } from "react";
|
||||
import { EditorWrapper } from "@/components/editors";
|
||||
import { EditorBubbleMenu } from "@/components/menus";
|
||||
// extensions
|
||||
import { SideMenuExtension } from "@/extensions";
|
||||
// plane editor imports
|
||||
import { RichTextEditorAdditionalExtensions } from "@/plane-editor/extensions/rich-text/extensions";
|
||||
import { SideMenuExtension, SlashCommands } from "@/extensions";
|
||||
// types
|
||||
import { EditorRefApi, IRichTextEditor } from "@/types";
|
||||
|
||||
const RichTextEditor = (props: IRichTextEditor) => {
|
||||
const {
|
||||
disabledExtensions,
|
||||
dragDropEnabled,
|
||||
fileHandler,
|
||||
bubbleMenuEnabled = true,
|
||||
extensions: externalExtensions = [],
|
||||
} = props;
|
||||
const { disabledExtensions, dragDropEnabled, bubbleMenuEnabled = true, extensions: externalExtensions = [] } = props;
|
||||
|
||||
const getExtensions = useCallback(() => {
|
||||
const extensions = [
|
||||
@@ -25,14 +17,17 @@ const RichTextEditor = (props: IRichTextEditor) => {
|
||||
aiEnabled: false,
|
||||
dragDropEnabled: !!dragDropEnabled,
|
||||
}),
|
||||
...RichTextEditorAdditionalExtensions({
|
||||
disabledExtensions,
|
||||
fileHandler,
|
||||
}),
|
||||
];
|
||||
if (!disabledExtensions?.includes("slash-commands")) {
|
||||
extensions.push(
|
||||
SlashCommands({
|
||||
disabledExtensions,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return extensions;
|
||||
}, [dragDropEnabled, disabledExtensions, externalExtensions, fileHandler]);
|
||||
}, [dragDropEnabled, disabledExtensions, externalExtensions]);
|
||||
|
||||
return (
|
||||
<EditorWrapper {...props} extensions={getExtensions()}>
|
||||
|
||||
@@ -1,33 +1,11 @@
|
||||
import { forwardRef, useCallback } from "react";
|
||||
// plane editor extensions
|
||||
import { RichTextReadOnlyEditorAdditionalExtensions } from "@/plane-editor/extensions/rich-text/read-only-extensions";
|
||||
import { forwardRef } from "react";
|
||||
// types
|
||||
import { EditorReadOnlyRefApi, IRichTextReadOnlyEditor } from "@/types";
|
||||
// local imports
|
||||
import { ReadOnlyEditorWrapper } from "../read-only-editor-wrapper";
|
||||
|
||||
const RichTextReadOnlyEditorWithRef = forwardRef<EditorReadOnlyRefApi, IRichTextReadOnlyEditor>((props, ref) => {
|
||||
const { disabledExtensions, fileHandler } = props;
|
||||
|
||||
const getExtensions = useCallback(() => {
|
||||
const extensions = [
|
||||
...RichTextReadOnlyEditorAdditionalExtensions({
|
||||
disabledExtensions,
|
||||
fileHandler,
|
||||
}),
|
||||
];
|
||||
|
||||
return extensions;
|
||||
}, [disabledExtensions, fileHandler]);
|
||||
|
||||
return (
|
||||
<ReadOnlyEditorWrapper
|
||||
{...props}
|
||||
extensions={getExtensions()}
|
||||
forwardedRef={ref as React.MutableRefObject<EditorReadOnlyRefApi | null>}
|
||||
/>
|
||||
);
|
||||
});
|
||||
const RichTextReadOnlyEditorWithRef = forwardRef<EditorReadOnlyRefApi, IRichTextReadOnlyEditor>((props, ref) => (
|
||||
<ReadOnlyEditorWrapper {...props} forwardedRef={ref as React.MutableRefObject<EditorReadOnlyRefApi | null>} />
|
||||
));
|
||||
|
||||
RichTextReadOnlyEditorWithRef.displayName = "RichReadOnlyEditorWithRef";
|
||||
|
||||
|
||||
@@ -1,46 +1,44 @@
|
||||
export const CORE_EXTENSIONS = {
|
||||
BLOCKQUOTE: "blockquote",
|
||||
BOLD: "bold",
|
||||
BULLET_LIST: "bulletList",
|
||||
CALLOUT: "calloutComponent",
|
||||
CHARACTER_COUNT: "characterCount",
|
||||
CODE_BLOCK: "codeBlock",
|
||||
CODE_INLINE: "code",
|
||||
CUSTOM_COLOR: "customColor",
|
||||
CUSTOM_IMAGE: "imageComponent",
|
||||
CUSTOM_LINK: "link",
|
||||
DOCUMENT: "doc",
|
||||
DROP_CURSOR: "dropCursor",
|
||||
ENTER_KEY: "enterKey",
|
||||
GAP_CURSOR: "gapCursor",
|
||||
HARD_BREAK: "hardBreak",
|
||||
HEADING: "heading",
|
||||
HEADINGS_LIST: "headingsList",
|
||||
HISTORY: "history",
|
||||
HORIZONTAL_RULE: "horizontalRule",
|
||||
IMAGE: "image",
|
||||
ITALIC: "italic",
|
||||
LIST_ITEM: "listItem",
|
||||
MARKDOWN_CLIPBOARD: "markdownClipboard",
|
||||
MENTION: "mention",
|
||||
ORDERED_LIST: "orderedList",
|
||||
PARAGRAPH: "paragraph",
|
||||
PLACEHOLDER: "placeholder",
|
||||
SIDE_MENU: "editorSideMenu",
|
||||
SLASH_COMMANDS: "slash-command",
|
||||
STRIKETHROUGH: "strike",
|
||||
TABLE: "table",
|
||||
TABLE_CELL: "tableCell",
|
||||
TABLE_HEADER: "tableHeader",
|
||||
TABLE_ROW: "tableRow",
|
||||
TASK_ITEM: "taskItem",
|
||||
TASK_LIST: "taskList",
|
||||
TEXT_ALIGN: "textAlign",
|
||||
TEXT_STYLE: "textStyle",
|
||||
TYPOGRAPHY: "typography",
|
||||
UNDERLINE: "underline",
|
||||
UTILITY: "utility",
|
||||
WORK_ITEM_EMBED: "issue-embed-component",
|
||||
} as const;
|
||||
|
||||
export type CORE_EXTENSIONS = typeof CORE_EXTENSIONS[keyof typeof CORE_EXTENSIONS];
|
||||
export enum CORE_EXTENSIONS {
|
||||
BLOCKQUOTE = "blockquote",
|
||||
BOLD = "bold",
|
||||
BULLET_LIST = "bulletList",
|
||||
CALLOUT = "calloutComponent",
|
||||
CHARACTER_COUNT = "characterCount",
|
||||
CODE_BLOCK = "codeBlock",
|
||||
CODE_INLINE = "code",
|
||||
CUSTOM_COLOR = "customColor",
|
||||
CUSTOM_IMAGE = "imageComponent",
|
||||
CUSTOM_LINK = "link",
|
||||
DOCUMENT = "doc",
|
||||
DROP_CURSOR = "dropCursor",
|
||||
ENTER_KEY = "enterKey",
|
||||
GAP_CURSOR = "gapCursor",
|
||||
HARD_BREAK = "hardBreak",
|
||||
HEADING = "heading",
|
||||
HEADINGS_LIST = "headingsList",
|
||||
HISTORY = "history",
|
||||
HORIZONTAL_RULE = "horizontalRule",
|
||||
IMAGE = "image",
|
||||
ITALIC = "italic",
|
||||
LIST_ITEM = "listItem",
|
||||
MARKDOWN_CLIPBOARD = "markdownClipboard",
|
||||
MENTION = "mention",
|
||||
ORDERED_LIST = "orderedList",
|
||||
PARAGRAPH = "paragraph",
|
||||
PLACEHOLDER = "placeholder",
|
||||
SIDE_MENU = "editorSideMenu",
|
||||
SLASH_COMMANDS = "slash-command",
|
||||
STRIKETHROUGH = "strike",
|
||||
TABLE = "table",
|
||||
TABLE_CELL = "tableCell",
|
||||
TABLE_HEADER = "tableHeader",
|
||||
TABLE_ROW = "tableRow",
|
||||
TASK_ITEM = "taskItem",
|
||||
TASK_LIST = "taskList",
|
||||
TEXT_ALIGN = "textAlign",
|
||||
TEXT_STYLE = "textStyle",
|
||||
TYPOGRAPHY = "typography",
|
||||
UNDERLINE = "underline",
|
||||
UTILITY = "utility",
|
||||
WORK_ITEM_EMBED = "issue-embed-component",
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
export const CORE_EDITOR_META = {
|
||||
SKIP_FILE_DELETION: "skipFileDeletion",
|
||||
} as const;
|
||||
|
||||
export type CORE_EDITOR_META = typeof CORE_EDITOR_META[keyof typeof CORE_EDITOR_META];
|
||||
export enum CORE_EDITOR_META {
|
||||
SKIP_FILE_DELETION = "skipFileDeletion",
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
export const EAttributeNames = {
|
||||
ICON_COLOR: "data-icon-color",
|
||||
ICON_NAME: "data-icon-name",
|
||||
EMOJI_UNICODE: "data-emoji-unicode",
|
||||
EMOJI_URL: "data-emoji-url",
|
||||
LOGO_IN_USE: "data-logo-in-use",
|
||||
BACKGROUND: "data-background",
|
||||
BLOCK_TYPE: "data-block-type",
|
||||
} as const;
|
||||
|
||||
export type EAttributeNames = typeof EAttributeNames[keyof typeof EAttributeNames];
|
||||
export enum EAttributeNames {
|
||||
ICON_COLOR = "data-icon-color",
|
||||
ICON_NAME = "data-icon-name",
|
||||
EMOJI_UNICODE = "data-emoji-unicode",
|
||||
EMOJI_URL = "data-emoji-url",
|
||||
LOGO_IN_USE = "data-logo-in-use",
|
||||
BACKGROUND = "data-background",
|
||||
BLOCK_TYPE = "data-block-type",
|
||||
}
|
||||
|
||||
export type TCalloutBlockIconAttributes = {
|
||||
[EAttributeNames.ICON_COLOR]: string | undefined;
|
||||
|
||||
@@ -170,9 +170,8 @@ export const CoreEditorExtensions = (args: TArguments): Extensions => {
|
||||
CustomTextAlignExtension,
|
||||
CustomCalloutExtension,
|
||||
UtilityExtension({
|
||||
disabledExtensions,
|
||||
fileHandler,
|
||||
isEditable: editable,
|
||||
fileHandler,
|
||||
}),
|
||||
CustomColorExtension,
|
||||
...CoreEditorAdditionalExtensions({
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
// plane types
|
||||
import { TSearchEntities } from "@plane/types";
|
||||
|
||||
export const EMentionComponentAttributeNames = {
|
||||
ID: "id",
|
||||
ENTITY_IDENTIFIER: "entity_identifier",
|
||||
ENTITY_NAME: "entity_name",
|
||||
} as const;
|
||||
|
||||
export type EMentionComponentAttributeNames = typeof EMentionComponentAttributeNames[keyof typeof EMentionComponentAttributeNames];
|
||||
export enum EMentionComponentAttributeNames {
|
||||
ID = "id",
|
||||
ENTITY_IDENTIFIER = "entity_identifier",
|
||||
ENTITY_NAME = "entity_name",
|
||||
}
|
||||
|
||||
export type TMentionComponentAttributes = {
|
||||
[EMentionComponentAttributeNames.ID]: string | null;
|
||||
|
||||
@@ -127,9 +127,8 @@ export const CoreReadOnlyEditorExtensions = (props: Props): Extensions => {
|
||||
CustomTextAlignExtension,
|
||||
CustomCalloutReadOnlyExtension,
|
||||
UtilityExtension({
|
||||
disabledExtensions,
|
||||
fileHandler,
|
||||
isEditable: false,
|
||||
fileHandler,
|
||||
}),
|
||||
...CoreReadOnlyEditorAdditionalExtensions({
|
||||
disabledExtensions,
|
||||
|
||||
@@ -8,7 +8,7 @@ import { DropHandlerPlugin } from "@/plugins/drop";
|
||||
import { FilePlugins } from "@/plugins/file/root";
|
||||
import { MarkdownClipboardPlugin } from "@/plugins/markdown-clipboard";
|
||||
// types
|
||||
import { TExtensions, TFileHandler, TReadOnlyFileHandler } from "@/types";
|
||||
import { TFileHandler, TReadOnlyFileHandler } from "@/types";
|
||||
|
||||
declare module "@tiptap/core" {
|
||||
interface Commands {
|
||||
@@ -24,14 +24,13 @@ export interface UtilityExtensionStorage {
|
||||
}
|
||||
|
||||
type Props = {
|
||||
disabledExtensions: TExtensions[];
|
||||
fileHandler: TFileHandler | TReadOnlyFileHandler;
|
||||
isEditable: boolean;
|
||||
};
|
||||
|
||||
export const UtilityExtension = (props: Props) => {
|
||||
const { disabledExtensions, fileHandler, isEditable } = props;
|
||||
const { restore } = fileHandler;
|
||||
const { fileHandler, isEditable } = props;
|
||||
const { restore: restoreImageFn } = fileHandler;
|
||||
|
||||
return Extension.create<Record<string, unknown>, UtilityExtensionStorage>({
|
||||
name: "utility",
|
||||
@@ -46,15 +45,12 @@ export const UtilityExtension = (props: Props) => {
|
||||
}),
|
||||
...codemark({ markType: this.editor.schema.marks.code }),
|
||||
MarkdownClipboardPlugin(this.editor),
|
||||
DropHandlerPlugin({
|
||||
disabledExtensions,
|
||||
editor: this.editor,
|
||||
}),
|
||||
DropHandlerPlugin(this.editor),
|
||||
];
|
||||
},
|
||||
|
||||
onCreate() {
|
||||
restorePublicImages(this.editor, restore);
|
||||
restorePublicImages(this.editor, restoreImageFn);
|
||||
},
|
||||
|
||||
addStorage() {
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
export const EFileError = {
|
||||
INVALID_FILE_TYPE: "INVALID_FILE_TYPE",
|
||||
FILE_SIZE_TOO_LARGE: "FILE_SIZE_TOO_LARGE",
|
||||
NO_FILE_SELECTED: "NO_FILE_SELECTED",
|
||||
} as const;
|
||||
|
||||
export type EFileError = typeof EFileError[keyof typeof EFileError];
|
||||
export enum EFileError {
|
||||
INVALID_FILE_TYPE = "INVALID_FILE_TYPE",
|
||||
FILE_SIZE_TOO_LARGE = "FILE_SIZE_TOO_LARGE",
|
||||
NO_FILE_SELECTED = "NO_FILE_SELECTED",
|
||||
}
|
||||
|
||||
type TArgs = {
|
||||
acceptedMimeTypes: string[];
|
||||
|
||||
@@ -92,8 +92,7 @@ export const useCollaborativeEditor = (props: TCollaborativeEditorProps) => {
|
||||
...(extensions ?? []),
|
||||
...DocumentEditorAdditionalExtensions({
|
||||
disabledExtensions,
|
||||
embedConfig: embedHandler,
|
||||
fileHandler,
|
||||
issueEmbedConfig: embedHandler?.issue,
|
||||
provider,
|
||||
userDetails: user,
|
||||
}),
|
||||
|
||||
@@ -3,17 +3,10 @@ import { Plugin, PluginKey } from "@tiptap/pm/state";
|
||||
// constants
|
||||
import { ACCEPTED_ATTACHMENT_MIME_TYPES, ACCEPTED_IMAGE_MIME_TYPES } from "@/constants/config";
|
||||
// types
|
||||
import { TEditorCommands, TExtensions } from "@/types";
|
||||
import { TEditorCommands } from "@/types";
|
||||
|
||||
type Props = {
|
||||
disabledExtensions?: TExtensions[];
|
||||
editor: Editor;
|
||||
};
|
||||
|
||||
export const DropHandlerPlugin = (props: Props): Plugin => {
|
||||
const { disabledExtensions, editor } = props;
|
||||
|
||||
return new Plugin({
|
||||
export const DropHandlerPlugin = (editor: Editor): Plugin =>
|
||||
new Plugin({
|
||||
key: new PluginKey("drop-handler-plugin"),
|
||||
props: {
|
||||
handlePaste: (view, event) => {
|
||||
@@ -32,7 +25,6 @@ export const DropHandlerPlugin = (props: Props): Plugin => {
|
||||
if (acceptedFiles.length) {
|
||||
const pos = view.state.selection.from;
|
||||
insertFilesSafely({
|
||||
disabledExtensions,
|
||||
editor,
|
||||
files: acceptedFiles,
|
||||
initialPos: pos,
|
||||
@@ -66,7 +58,6 @@ export const DropHandlerPlugin = (props: Props): Plugin => {
|
||||
if (coordinates) {
|
||||
const pos = coordinates.pos;
|
||||
insertFilesSafely({
|
||||
disabledExtensions,
|
||||
editor,
|
||||
files: acceptedFiles,
|
||||
initialPos: pos,
|
||||
@@ -80,10 +71,8 @@ export const DropHandlerPlugin = (props: Props): Plugin => {
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
type InsertFilesSafelyArgs = {
|
||||
disabledExtensions?: TExtensions[];
|
||||
editor: Editor;
|
||||
event: "insert" | "drop";
|
||||
files: File[];
|
||||
@@ -92,7 +81,7 @@ type InsertFilesSafelyArgs = {
|
||||
};
|
||||
|
||||
export const insertFilesSafely = async (args: InsertFilesSafelyArgs) => {
|
||||
const { disabledExtensions, editor, event, files, initialPos, type } = args;
|
||||
const { editor, event, files, initialPos, type } = args;
|
||||
let pos = initialPos;
|
||||
|
||||
for (const file of files) {
|
||||
@@ -111,7 +100,7 @@ export const insertFilesSafely = async (args: InsertFilesSafelyArgs) => {
|
||||
else if (ACCEPTED_ATTACHMENT_MIME_TYPES.includes(file.type)) fileType = "attachment";
|
||||
}
|
||||
// insert file depending on the type at the current position
|
||||
if (fileType === "image" && !disabledExtensions?.includes("image")) {
|
||||
if (fileType === "image") {
|
||||
editor.commands.insertImageComponent({
|
||||
file,
|
||||
pos,
|
||||
|
||||
@@ -160,7 +160,6 @@ export interface IReadOnlyEditorProps {
|
||||
disabledExtensions: TExtensions[];
|
||||
displayConfig?: TDisplayConfig;
|
||||
editorClassName?: string;
|
||||
extensions?: Extensions;
|
||||
fileHandler: TReadOnlyFileHandler;
|
||||
forwardedRef?: React.MutableRefObject<EditorReadOnlyRefApi | null>;
|
||||
id: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@plane/eslint-config",
|
||||
"private": true,
|
||||
"version": "0.26.1",
|
||||
"version": "0.26.0",
|
||||
"license": "AGPL-3.0",
|
||||
"files": [
|
||||
"library.js",
|
||||
@@ -18,6 +18,6 @@
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"typescript": "5.8.3"
|
||||
"typescript": "5.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@plane/hooks",
|
||||
"version": "0.26.1",
|
||||
"version": "0.26.0",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "React hooks that are shared across multiple apps internally",
|
||||
"private": true,
|
||||
@@ -23,6 +23,6 @@
|
||||
"@types/node": "^22.5.4",
|
||||
"@types/react": "^18.3.11",
|
||||
"tsup": "8.4.0",
|
||||
"typescript": "5.8.3"
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@plane/i18n",
|
||||
"version": "0.26.1",
|
||||
"version": "0.26.0",
|
||||
"license": "AGPL-3.0",
|
||||
"description": "I18n shared across multiple apps internally",
|
||||
"private": true,
|
||||
@@ -17,6 +17,6 @@
|
||||
"devDependencies": {
|
||||
"@plane/eslint-config": "*",
|
||||
"@types/node": "^22.5.4",
|
||||
"typescript": "5.8.3"
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,12 +28,9 @@ export const SUPPORTED_LANGUAGES: ILanguageOption[] = [
|
||||
* Enum for translation file names
|
||||
* These are the JSON files that contain translations each category
|
||||
*/
|
||||
export const ETranslationFiles = {
|
||||
TRANSLATIONS: "translations",
|
||||
ACCESSIBILITY: "accessibility",
|
||||
EDITOR: "editor",
|
||||
} as const;
|
||||
|
||||
export type ETranslationFiles = typeof ETranslationFiles[keyof typeof ETranslationFiles];
|
||||
export enum ETranslationFiles {
|
||||
TRANSLATIONS = "translations",
|
||||
ACCESSIBILITY = "accessibility",
|
||||
}
|
||||
|
||||
export const LANGUAGE_STORAGE_KEY = "userLanguage";
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -866,21 +866,7 @@
|
||||
"view": "Pohled",
|
||||
"deactivated_user": "Deaktivovaný uživatel",
|
||||
"apply": "Použít",
|
||||
"applying": "Používání",
|
||||
"users": "Uživatelé",
|
||||
"admins": "Administrátoři",
|
||||
"guests": "Hosté",
|
||||
"on_track": "Na správné cestě",
|
||||
"off_track": "Mimo plán",
|
||||
"at_risk": "V ohrožení",
|
||||
"timeline": "Časová osa",
|
||||
"completion": "Dokončení",
|
||||
"upcoming": "Nadcházející",
|
||||
"completed": "Dokončeno",
|
||||
"in_progress": "Probíhá",
|
||||
"planned": "Plánováno",
|
||||
"paused": "Pozastaveno",
|
||||
"no_of": "Počet {entity}"
|
||||
"applying": "Používání"
|
||||
},
|
||||
"chart": {
|
||||
"x_axis": "Osa X",
|
||||
@@ -1330,6 +1316,19 @@
|
||||
"custom": "Vlastní analytika"
|
||||
},
|
||||
"empty_state": {
|
||||
"general": {
|
||||
"title": "Sledujte pokrok, vytížení a alokace. Identifikujte trendy, odstraňte překážky a zrychlete práci",
|
||||
"description": "Sledujte rozsah vs. poptávku, odhady a rozsah. Zjistěte výkonnost členů a týmů, zajistěte včasné dokončení projektů.",
|
||||
"primary_button": {
|
||||
"text": "Začněte první projekt",
|
||||
"comic": {
|
||||
"title": "Analytika funguje nejlépe s Cykly + Moduly",
|
||||
"description": "Nejprve časově ohraničte práci do Cyklů a seskupte položky přesahující cyklus do Modulů. Najdete je v levém menu."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"empty_state_v2": {
|
||||
"customized_insights": {
|
||||
"description": "Pracovní položky přiřazené vám, rozdělené podle stavu, se zde zobrazí.",
|
||||
"title": "Zatím žádná data"
|
||||
@@ -1345,22 +1344,21 @@
|
||||
},
|
||||
"created_vs_resolved": "Vytvořeno vs Vyřešeno",
|
||||
"customized_insights": "Přizpůsobené přehledy",
|
||||
"backlog_work_items": "Backlog {entity}",
|
||||
"backlog_work_items": "Pracovní položky v backlogu",
|
||||
"active_projects": "Aktivní projekty",
|
||||
"trend_on_charts": "Trend na grafech",
|
||||
"all_projects": "Všechny projekty",
|
||||
"summary_of_projects": "Souhrn projektů",
|
||||
"project_insights": "Přehled projektu",
|
||||
"started_work_items": "Zahájené {entity}",
|
||||
"total_work_items": "Celkový počet {entity}",
|
||||
"started_work_items": "Zahájené pracovní položky",
|
||||
"total_work_items": "Celkový počet pracovních položek",
|
||||
"total_projects": "Celkový počet projektů",
|
||||
"total_admins": "Celkový počet administrátorů",
|
||||
"total_users": "Celkový počet uživatelů",
|
||||
"total_intake": "Celkový příjem",
|
||||
"un_started_work_items": "Nezahájené {entity}",
|
||||
"un_started_work_items": "Nezahájené pracovní položky",
|
||||
"total_guests": "Celkový počet hostů",
|
||||
"completed_work_items": "Dokončené {entity}",
|
||||
"total": "Celkový počet {entity}"
|
||||
"completed_work_items": "Dokončené pracovní položky"
|
||||
},
|
||||
"workspace_projects": {
|
||||
"label": "{count, plural, one {Projekt} few {Projekty} other {Projektů}}",
|
||||
@@ -2464,9 +2462,5 @@
|
||||
"last_edited_by": "Naposledy upraveno uživatelem",
|
||||
"previously_edited_by": "Dříve upraveno uživatelem",
|
||||
"edited_by": "Upraveno uživatelem"
|
||||
},
|
||||
"self_hosted_maintenance_message": {
|
||||
"plane_didnt_start_up_this_could_be_because_one_or_more_plane_services_failed_to_start": "Plane se nespustil. To může být způsobeno tím, že se jeden nebo více služeb Plane nepodařilo spustit.",
|
||||
"choose_view_logs_from_setup_sh_and_docker_logs_to_be_sure": "Vyberte View Logs z setup.sh a Docker logů, abyste si byli jisti."
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -866,21 +866,7 @@
|
||||
"view": "Ansicht",
|
||||
"deactivated_user": "Deaktivierter Benutzer",
|
||||
"apply": "Anwenden",
|
||||
"applying": "Wird angewendet",
|
||||
"users": "Benutzer",
|
||||
"admins": "Administratoren",
|
||||
"guests": "Gäste",
|
||||
"on_track": "Im Plan",
|
||||
"off_track": "Außer Plan",
|
||||
"at_risk": "Gefährdet",
|
||||
"timeline": "Zeitleiste",
|
||||
"completion": "Fertigstellung",
|
||||
"upcoming": "Bevorstehend",
|
||||
"completed": "Abgeschlossen",
|
||||
"in_progress": "In Bearbeitung",
|
||||
"planned": "Geplant",
|
||||
"paused": "Pausiert",
|
||||
"no_of": "Anzahl {entity}"
|
||||
"applying": "Wird angewendet"
|
||||
},
|
||||
"chart": {
|
||||
"x_axis": "X-Achse",
|
||||
@@ -1330,6 +1316,19 @@
|
||||
"custom": "Benutzerdefinierte Analysen"
|
||||
},
|
||||
"empty_state": {
|
||||
"general": {
|
||||
"title": "Verfolgen Sie Fortschritt, Auslastung und Zuordnungen. Erkennen Sie Trends, entfernen Sie Blocker und beschleunigen Sie die Arbeit",
|
||||
"description": "Behalten Sie Umfang vs. Nachfrage, Schätzungen und Umfang im Blick. Verfolgen Sie die Leistung von Mitgliedern und Teams, um sicherzustellen, dass Projekte pünktlich abgeschlossen werden.",
|
||||
"primary_button": {
|
||||
"text": "Erstes Projekt starten",
|
||||
"comic": {
|
||||
"title": "Analysen funktionieren am besten mit Zyklen + Modulen",
|
||||
"description": "Begrenzen Sie zuerst Arbeit zeitlich in Zyklen und gruppieren Sie die übergreifenden Elemente in Module. Sie finden sie im linken Menü."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"empty_state_v2": {
|
||||
"customized_insights": {
|
||||
"description": "Ihnen zugewiesene Arbeitselemente, aufgeschlüsselt nach Status, werden hier angezeigt.",
|
||||
"title": "Noch keine Daten"
|
||||
@@ -1345,22 +1344,21 @@
|
||||
},
|
||||
"created_vs_resolved": "Erstellt vs Gelöst",
|
||||
"customized_insights": "Individuelle Einblicke",
|
||||
"backlog_work_items": "Backlog-{entity}",
|
||||
"backlog_work_items": "Backlog-Arbeitselemente",
|
||||
"active_projects": "Aktive Projekte",
|
||||
"trend_on_charts": "Trend in Diagrammen",
|
||||
"all_projects": "Alle Projekte",
|
||||
"summary_of_projects": "Projektübersicht",
|
||||
"project_insights": "Projekteinblicke",
|
||||
"started_work_items": "Begonnene {entity}",
|
||||
"total_work_items": "Gesamte {entity}",
|
||||
"started_work_items": "Begonnene Arbeitselemente",
|
||||
"total_work_items": "Gesamte Arbeitselemente",
|
||||
"total_projects": "Gesamtprojekte",
|
||||
"total_admins": "Gesamtanzahl der Admins",
|
||||
"total_users": "Gesamtanzahl der Benutzer",
|
||||
"total_intake": "Gesamteinnahmen",
|
||||
"un_started_work_items": "Nicht begonnene {entity}",
|
||||
"un_started_work_items": "Nicht begonnene Arbeitselemente",
|
||||
"total_guests": "Gesamtanzahl der Gäste",
|
||||
"completed_work_items": "Abgeschlossene {entity}",
|
||||
"total": "Gesamte {entity}"
|
||||
"completed_work_items": "Abgeschlossene Arbeitselemente"
|
||||
},
|
||||
"workspace_projects": {
|
||||
"label": "{count, plural, one {Projekt} few {Projekte} other {Projekte}}",
|
||||
@@ -2463,9 +2461,5 @@
|
||||
"last_edited_by": "Zuletzt bearbeitet von",
|
||||
"previously_edited_by": "Zuvor bearbeitet von",
|
||||
"edited_by": "Bearbeitet von"
|
||||
},
|
||||
"self_hosted_maintenance_message": {
|
||||
"plane_didnt_start_up_this_could_be_because_one_or_more_plane_services_failed_to_start": "Plane ist nicht gestartet. Dies könnte daran liegen, dass einer oder mehrere Plane-Services nicht starten konnten.",
|
||||
"choose_view_logs_from_setup_sh_and_docker_logs_to_be_sure": "Wählen Sie View Logs aus setup.sh und Docker-Logs, um sicherzugehen."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -476,9 +476,6 @@
|
||||
"modules": "Modules",
|
||||
"labels": "Labels",
|
||||
"label": "Label",
|
||||
"admins": "Admins",
|
||||
"users": "Users",
|
||||
"guests": "Guests",
|
||||
"assignees": "Assignees",
|
||||
"assignee": "Assignee",
|
||||
"created_by": "Created by",
|
||||
@@ -615,16 +612,6 @@
|
||||
"quarter": "Quarter",
|
||||
"press_for_commands": "Press '/' for commands",
|
||||
"click_to_add_description": "Click to add description",
|
||||
"on_track": "On-Track",
|
||||
"off_track": "Off-Track",
|
||||
"at_risk": "At risk",
|
||||
"timeline": "Timeline",
|
||||
"completion": "Completion",
|
||||
"upcoming": "Upcoming",
|
||||
"completed": "Completed",
|
||||
"in_progress": "In progress",
|
||||
"planned": "Planned",
|
||||
"paused": "Paused",
|
||||
"search": {
|
||||
"label": "Search",
|
||||
"placeholder": "Type to search",
|
||||
@@ -722,8 +709,7 @@
|
||||
"deactivated_user": "Deactivated user",
|
||||
"apply": "Apply",
|
||||
"applying": "Applying",
|
||||
"overview": "Overview",
|
||||
"no_of": "No. of {entity}"
|
||||
"overview": "Overview"
|
||||
},
|
||||
"chart": {
|
||||
"x_axis": "X-axis",
|
||||
@@ -1172,11 +1158,29 @@
|
||||
"scope_and_demand": "Scope and Demand",
|
||||
"custom": "Custom Analytics"
|
||||
},
|
||||
"total": "Total {entity}",
|
||||
"started_work_items": "Started {entity}",
|
||||
"backlog_work_items": "Backlog {entity}",
|
||||
"un_started_work_items": "Unstarted {entity}",
|
||||
"completed_work_items": "Completed {entity}",
|
||||
"empty_state": {
|
||||
"general": {
|
||||
"title": "Track progress, workloads, and allocations. Spot trends, remove blockers, and move work faster",
|
||||
"description": "See scope versus demand, estimates, and scope creep. Get performance by team members and teams, and make sure your project runs on time.",
|
||||
"primary_button": {
|
||||
"text": "Start your first project",
|
||||
"comic": {
|
||||
"title": "Analytics works best with Cycles + Modules",
|
||||
"description": "First, timebox your work items into Cycles and, if you can, group work items that span more than a cycle into Modules. Check out both on the left nav."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"total_work_items": "Total work items",
|
||||
"started_work_items": "Started work items",
|
||||
"backlog_work_items": "Backlog work items",
|
||||
"un_started_work_items": "Unstarted work items",
|
||||
"completed_work_items": "Completed work items",
|
||||
"total_guests": "Total Guests",
|
||||
"total_intake": "Total Intake",
|
||||
"total_users": "Total Users",
|
||||
"total_admins": "Total Admins",
|
||||
"total_projects": "Total Projects",
|
||||
"project_insights": "Project Insights",
|
||||
"summary_of_projects": "Summary of Projects",
|
||||
"all_projects": "All Projects",
|
||||
@@ -1184,7 +1188,7 @@
|
||||
"active_projects": "Active Projects",
|
||||
"customized_insights": "Customized Insights",
|
||||
"created_vs_resolved": "Created vs Resolved",
|
||||
"empty_state": {
|
||||
"empty_state_v2": {
|
||||
"project_insights": {
|
||||
"title": "No data yet",
|
||||
"description": "Work items assigned to you, broken down by state, will show up here."
|
||||
@@ -1308,23 +1312,23 @@
|
||||
}
|
||||
},
|
||||
"account_settings": {
|
||||
"profile": {},
|
||||
"preferences": {
|
||||
"profile":{},
|
||||
"preferences":{
|
||||
"heading": "Preferences",
|
||||
"description": "Customize your app experience the way you work"
|
||||
},
|
||||
"notifications": {
|
||||
"notifications":{
|
||||
"heading": "Email notifications",
|
||||
"description": "Stay in the loop on Work items you are subscribed to. Enable this to get notified."
|
||||
"description": "Stay in the loop on Work items you are subscribed to. Enable this to get notified."
|
||||
},
|
||||
"security": {
|
||||
"security":{
|
||||
"heading": "Security"
|
||||
},
|
||||
"api_tokens": {
|
||||
"api_tokens":{
|
||||
"heading": "Personal Access Tokens",
|
||||
"description": "Generate secure API tokens to integrate your data with external systems and applications."
|
||||
},
|
||||
"activity": {
|
||||
"activity":{
|
||||
"heading": "Activity",
|
||||
"description": "Track your recent actions and changes across all projects and work items."
|
||||
}
|
||||
@@ -1396,7 +1400,7 @@
|
||||
},
|
||||
"billing_and_plans": {
|
||||
"heading": "Billing & Plans",
|
||||
"description": "Choose your plan, manage subscriptions, and easily upgrade as your needs grow.",
|
||||
"description":"Choose your plan, manage subscriptions, and easily upgrade as your needs grow.",
|
||||
"title": "Billing & Plans",
|
||||
"current_plan": "Current plan",
|
||||
"free_plan": "You are currently using the free plan",
|
||||
@@ -2340,9 +2344,5 @@
|
||||
"last_edited_by": "Last edited by",
|
||||
"previously_edited_by": "Previously edited by",
|
||||
"edited_by": "Edited by"
|
||||
},
|
||||
"self_hosted_maintenance_message": {
|
||||
"plane_didnt_start_up_this_could_be_because_one_or_more_plane_services_failed_to_start": "Plane didn't start up. This could be because one or more Plane services failed to start.",
|
||||
"choose_view_logs_from_setup_sh_and_docker_logs_to_be_sure": "Choose View Logs from setup.sh and Docker logs to be sure."
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -869,21 +869,7 @@
|
||||
"view": "Ver",
|
||||
"deactivated_user": "Usuario desactivado",
|
||||
"apply": "Aplicar",
|
||||
"applying": "Aplicando",
|
||||
"users": "Usuarios",
|
||||
"admins": "Administradores",
|
||||
"guests": "Invitados",
|
||||
"on_track": "En camino",
|
||||
"off_track": "Fuera de camino",
|
||||
"at_risk": "En riesgo",
|
||||
"timeline": "Cronograma",
|
||||
"completion": "Finalización",
|
||||
"upcoming": "Próximo",
|
||||
"completed": "Completado",
|
||||
"in_progress": "En progreso",
|
||||
"planned": "Planificado",
|
||||
"paused": "Pausado",
|
||||
"no_of": "N.º de {entity}"
|
||||
"applying": "Aplicando"
|
||||
},
|
||||
"chart": {
|
||||
"x_axis": "Eje X",
|
||||
@@ -1333,6 +1319,19 @@
|
||||
"custom": "Análisis Personalizado"
|
||||
},
|
||||
"empty_state": {
|
||||
"general": {
|
||||
"title": "Rastrea el progreso, cargas de trabajo y asignaciones. Identifica tendencias, elimina bloqueos y mueve el trabajo más rápido",
|
||||
"description": "Observa el alcance versus la demanda, estimaciones y el aumento del alcance. Obtén el rendimiento por miembros del equipo y equipos, y asegúrate de que tu proyecto se ejecute a tiempo.",
|
||||
"primary_button": {
|
||||
"text": "Inicia tu primer proyecto",
|
||||
"comic": {
|
||||
"title": "El análisis funciona mejor con Ciclos + Módulos",
|
||||
"description": "Primero, organiza tus elementos de trabajo en Ciclos y, si puedes, agrupa los elementos de trabajo que abarcan más de un ciclo en Módulos. Revisa ambos en la navegación izquierda."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"empty_state_v2": {
|
||||
"customized_insights": {
|
||||
"description": "Los elementos de trabajo asignados a ti, desglosados por estado, aparecerán aquí.",
|
||||
"title": "Aún no hay datos"
|
||||
@@ -1348,22 +1347,21 @@
|
||||
},
|
||||
"created_vs_resolved": "Creado vs Resuelto",
|
||||
"customized_insights": "Información personalizada",
|
||||
"backlog_work_items": "{entity} en backlog",
|
||||
"backlog_work_items": "Elementos de trabajo en backlog",
|
||||
"active_projects": "Proyectos activos",
|
||||
"trend_on_charts": "Tendencia en gráficos",
|
||||
"all_projects": "Todos los proyectos",
|
||||
"summary_of_projects": "Resumen de proyectos",
|
||||
"project_insights": "Información del proyecto",
|
||||
"started_work_items": "{entity} iniciados",
|
||||
"total_work_items": "Total de {entity}",
|
||||
"started_work_items": "Elementos de trabajo iniciados",
|
||||
"total_work_items": "Total de elementos de trabajo",
|
||||
"total_projects": "Total de proyectos",
|
||||
"total_admins": "Total de administradores",
|
||||
"total_users": "Total de usuarios",
|
||||
"total_intake": "Ingreso total",
|
||||
"un_started_work_items": "{entity} no iniciados",
|
||||
"un_started_work_items": "Elementos de trabajo no iniciados",
|
||||
"total_guests": "Total de invitados",
|
||||
"completed_work_items": "{entity} completados",
|
||||
"total": "Total de {entity}"
|
||||
"completed_work_items": "Elementos de trabajo completados"
|
||||
},
|
||||
"workspace_projects": {
|
||||
"label": "{count, plural, one {Proyecto} other {Proyectos}}",
|
||||
@@ -2466,9 +2464,5 @@
|
||||
"last_edited_by": "Última edición por",
|
||||
"previously_edited_by": "Editado anteriormente por",
|
||||
"edited_by": "Editado por"
|
||||
},
|
||||
"self_hosted_maintenance_message": {
|
||||
"plane_didnt_start_up_this_could_be_because_one_or_more_plane_services_failed_to_start": "Plane no se inició. Esto podría deberse a que uno o más servicios de Plane fallaron al iniciar.",
|
||||
"choose_view_logs_from_setup_sh_and_docker_logs_to_be_sure": "Selecciona View Logs desde setup.sh y los logs de Docker para estar seguro."
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -867,21 +867,7 @@
|
||||
"view": "Afficher",
|
||||
"deactivated_user": "Utilisateur désactivé",
|
||||
"apply": "Appliquer",
|
||||
"applying": "Application",
|
||||
"users": "Utilisateurs",
|
||||
"admins": "Administrateurs",
|
||||
"guests": "Invités",
|
||||
"on_track": "Sur la bonne voie",
|
||||
"off_track": "Hors de la bonne voie",
|
||||
"at_risk": "À risque",
|
||||
"timeline": "Chronologie",
|
||||
"completion": "Achèvement",
|
||||
"upcoming": "À venir",
|
||||
"completed": "Terminé",
|
||||
"in_progress": "En cours",
|
||||
"planned": "Planifié",
|
||||
"paused": "En pause",
|
||||
"no_of": "Nº de {entity}"
|
||||
"applying": "Application"
|
||||
},
|
||||
"chart": {
|
||||
"x_axis": "Axe X",
|
||||
@@ -1331,6 +1317,19 @@
|
||||
"custom": "Analytique Personnalisée"
|
||||
},
|
||||
"empty_state": {
|
||||
"general": {
|
||||
"title": "Suivez les progrès, les charges de travail et les allocations. Repérez les tendances, supprimez les blocages et accélérez le travail",
|
||||
"description": "Visualisez la portée par rapport à la demande, les estimations et l'augmentation de la portée. Obtenez les performances par membres de l'équipe et équipes, et assurez-vous que votre projet se déroule dans les délais.",
|
||||
"primary_button": {
|
||||
"text": "Commencez votre premier projet",
|
||||
"comic": {
|
||||
"title": "L'analytique fonctionne mieux avec les Cycles + Modules",
|
||||
"description": "D'abord, planifiez vos éléments de travail dans des Cycles et, si possible, regroupez les éléments de travail qui s'étendent sur plus d'un cycle dans des Modules. Consultez les deux dans la navigation de gauche."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"empty_state_v2": {
|
||||
"customized_insights": {
|
||||
"description": "Les éléments de travail qui vous sont assignés, répartis par état, s'afficheront ici.",
|
||||
"title": "Pas encore de données"
|
||||
@@ -1346,22 +1345,21 @@
|
||||
},
|
||||
"created_vs_resolved": "Créé vs Résolu",
|
||||
"customized_insights": "Informations personnalisées",
|
||||
"backlog_work_items": "{entity} en backlog",
|
||||
"backlog_work_items": "Éléments de travail en backlog",
|
||||
"active_projects": "Projets actifs",
|
||||
"trend_on_charts": "Tendance sur les graphiques",
|
||||
"all_projects": "Tous les projets",
|
||||
"summary_of_projects": "Résumé des projets",
|
||||
"project_insights": "Aperçus du projet",
|
||||
"started_work_items": "{entity} commencés",
|
||||
"total_work_items": "Total des {entity}",
|
||||
"started_work_items": "Éléments de travail commencés",
|
||||
"total_work_items": "Total des éléments de travail",
|
||||
"total_projects": "Total des projets",
|
||||
"total_admins": "Total des administrateurs",
|
||||
"total_users": "Nombre total d'utilisateurs",
|
||||
"total_intake": "Revenu total",
|
||||
"un_started_work_items": "{entity} non commencés",
|
||||
"un_started_work_items": "Éléments de travail non commencés",
|
||||
"total_guests": "Nombre total d'invités",
|
||||
"completed_work_items": "{entity} terminés",
|
||||
"total": "Total des {entity}"
|
||||
"completed_work_items": "Éléments de travail terminés"
|
||||
},
|
||||
"workspace_projects": {
|
||||
"label": "{count, plural, one {Projet} other {Projets}}",
|
||||
@@ -2464,9 +2462,5 @@
|
||||
"last_edited_by": "Dernière modification par",
|
||||
"previously_edited_by": "Précédemment modifié par",
|
||||
"edited_by": "Modifié par"
|
||||
},
|
||||
"self_hosted_maintenance_message": {
|
||||
"plane_didnt_start_up_this_could_be_because_one_or_more_plane_services_failed_to_start": "Plane n'a pas démarré. Cela pourrait être dû au fait qu'un ou plusieurs services Plane ont échoué à démarrer.",
|
||||
"choose_view_logs_from_setup_sh_and_docker_logs_to_be_sure": "Choisissez View Logs depuis setup.sh et les logs Docker pour en être sûr."
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -866,21 +866,7 @@
|
||||
"view": "Lihat",
|
||||
"deactivated_user": "Pengguna dinonaktifkan",
|
||||
"apply": "Terapkan",
|
||||
"applying": "Terapkan",
|
||||
"users": "Pengguna",
|
||||
"admins": "Admin",
|
||||
"guests": "Tamu",
|
||||
"on_track": "Sesuai Jalur",
|
||||
"off_track": "Menyimpang",
|
||||
"at_risk": "Dalam risiko",
|
||||
"timeline": "Linimasa",
|
||||
"completion": "Penyelesaian",
|
||||
"upcoming": "Mendatang",
|
||||
"completed": "Selesai",
|
||||
"in_progress": "Sedang berlangsung",
|
||||
"planned": "Direncanakan",
|
||||
"paused": "Dijedaikan",
|
||||
"no_of": "Jumlah {entity}"
|
||||
"applying": "Terapkan"
|
||||
},
|
||||
"chart": {
|
||||
"x_axis": "Sumbu-X",
|
||||
@@ -1330,6 +1316,19 @@
|
||||
"custom": "Analitik Kustom"
|
||||
},
|
||||
"empty_state": {
|
||||
"general": {
|
||||
"title": "Lacak kemajuan, beban kerja, dan alokasi. Temukan tren, hilangkan penghalang, dan percepat pekerjaan",
|
||||
"description": "Lihat lingkup dibandingkan permintaan, perkiraan, dan lingkup cree. Dapatkan kinerja oleh anggota tim dan tim, dan pastikan proyek Anda berjalan tepat waktu.",
|
||||
"primary_button": {
|
||||
"text": "Mulai proyek pertama Anda",
|
||||
"comic": {
|
||||
"title": "Analitik bekerja terbaik dengan Siklus + Modul",
|
||||
"description": "Pertama, bagi item kerja Anda ke dalam Siklus dan, jika memungkinkan, kelompokkan item kerja yang menjangkau lebih dari satu siklus ke dalam Modul. Lihat kedua fungsi pada navigasi kiri."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"empty_state_v2": {
|
||||
"customized_insights": {
|
||||
"description": "Item pekerjaan yang ditugaskan kepada Anda, dipecah berdasarkan status, akan muncul di sini.",
|
||||
"title": "Belum ada data"
|
||||
@@ -1345,22 +1344,21 @@
|
||||
},
|
||||
"created_vs_resolved": "Dibuat vs Diselesaikan",
|
||||
"customized_insights": "Wawasan yang Disesuaikan",
|
||||
"backlog_work_items": "{entity} backlog",
|
||||
"backlog_work_items": "Item pekerjaan backlog",
|
||||
"active_projects": "Proyek Aktif",
|
||||
"trend_on_charts": "Tren pada grafik",
|
||||
"all_projects": "Semua Proyek",
|
||||
"summary_of_projects": "Ringkasan Proyek",
|
||||
"project_insights": "Wawasan Proyek",
|
||||
"started_work_items": "{entity} yang telah dimulai",
|
||||
"total_work_items": "Total {entity}",
|
||||
"started_work_items": "Item pekerjaan yang telah dimulai",
|
||||
"total_work_items": "Total item pekerjaan",
|
||||
"total_projects": "Total Proyek",
|
||||
"total_admins": "Total Admin",
|
||||
"total_users": "Total Pengguna",
|
||||
"total_intake": "Total Pemasukan",
|
||||
"un_started_work_items": "{entity} yang belum dimulai",
|
||||
"un_started_work_items": "Item pekerjaan yang belum dimulai",
|
||||
"total_guests": "Total Tamu",
|
||||
"completed_work_items": "{entity} yang telah selesai",
|
||||
"total": "Total {entity}"
|
||||
"completed_work_items": "Item pekerjaan yang telah selesai"
|
||||
},
|
||||
"workspace_projects": {
|
||||
"label": "{count, plural, one {Proyek} other {Proyek}}",
|
||||
@@ -2458,10 +2456,5 @@
|
||||
"last_edited_by": "Terakhir disunting oleh",
|
||||
"previously_edited_by": "Sebelumnya disunting oleh",
|
||||
"edited_by": "Disunting oleh"
|
||||
},
|
||||
"self_hosted_maintenance_message": {
|
||||
"plane_didnt_start_up_this_could_be_because_one_or_more_plane_services_failed_to_start": "Plane tidak berhasil dimulai. Ini bisa karena satu atau lebih layanan Plane gagal untuk dimulai.",
|
||||
"choose_view_logs_from_setup_sh_and_docker_logs_to_be_sure": "Pilih View Logs dari setup.sh dan log Docker untuk memastikan."
|
||||
},
|
||||
"no_of": "Jumlah {entity}"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -865,21 +865,7 @@
|
||||
"view": "Visualizza",
|
||||
"deactivated_user": "Utente disattivato",
|
||||
"apply": "Applica",
|
||||
"applying": "Applicazione",
|
||||
"users": "Utenti",
|
||||
"admins": "Amministratori",
|
||||
"guests": "Ospiti",
|
||||
"on_track": "In linea",
|
||||
"off_track": "Fuori rotta",
|
||||
"at_risk": "A rischio",
|
||||
"timeline": "Cronologia",
|
||||
"completion": "Completamento",
|
||||
"upcoming": "In arrivo",
|
||||
"completed": "Completato",
|
||||
"in_progress": "In corso",
|
||||
"planned": "Pianificato",
|
||||
"paused": "In pausa",
|
||||
"no_of": "N. di {entity}"
|
||||
"applying": "Applicazione"
|
||||
},
|
||||
"chart": {
|
||||
"x_axis": "Asse X",
|
||||
@@ -1329,6 +1315,19 @@
|
||||
"custom": "Analisi personalizzata"
|
||||
},
|
||||
"empty_state": {
|
||||
"general": {
|
||||
"title": "Traccia il progresso, i carichi di lavoro e le assegnazioni. Individua tendenze, rimuovi gli ostacoli e accelera il lavoro",
|
||||
"description": "Visualizza l'ambito rispetto alla domanda, le stime e il fenomeno del scope creep. Ottieni le prestazioni dei membri del team e dei team, e assicurati che il tuo progetto rispetti le scadenze.",
|
||||
"primary_button": {
|
||||
"text": "Inizia il tuo primo progetto",
|
||||
"comic": {
|
||||
"title": "Le analisi funzionano meglio con Cicli + Moduli",
|
||||
"description": "Prima, definisci i tuoi elementi di lavoro in cicli e, se puoi, raggruppa quelli che si estendono per più di un ciclo in moduli. Dai un'occhiata ad entrambi nel menu di sinistra."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"empty_state_v2": {
|
||||
"customized_insights": {
|
||||
"description": "Gli elementi di lavoro assegnati a te, suddivisi per stato, verranno visualizzati qui.",
|
||||
"title": "Nessun dato disponibile"
|
||||
@@ -1344,22 +1343,21 @@
|
||||
},
|
||||
"created_vs_resolved": "Creato vs Risolto",
|
||||
"customized_insights": "Approfondimenti personalizzati",
|
||||
"backlog_work_items": "{entity} nel backlog",
|
||||
"backlog_work_items": "Elementi di lavoro nel backlog",
|
||||
"active_projects": "Progetti attivi",
|
||||
"trend_on_charts": "Tendenza nei grafici",
|
||||
"all_projects": "Tutti i progetti",
|
||||
"summary_of_projects": "Riepilogo dei progetti",
|
||||
"project_insights": "Approfondimenti sul progetto",
|
||||
"started_work_items": "{entity} iniziati",
|
||||
"total_work_items": "Totale {entity}",
|
||||
"started_work_items": "Elementi di lavoro iniziati",
|
||||
"total_work_items": "Totale elementi di lavoro",
|
||||
"total_projects": "Progetti totali",
|
||||
"total_admins": "Totale amministratori",
|
||||
"total_users": "Totale utenti",
|
||||
"total_intake": "Entrate totali",
|
||||
"un_started_work_items": "{entity} non avviati",
|
||||
"un_started_work_items": "Elementi di lavoro non avviati",
|
||||
"total_guests": "Totale ospiti",
|
||||
"completed_work_items": "{entity} completati",
|
||||
"total": "Totale {entity}"
|
||||
"completed_work_items": "Elementi di lavoro completati"
|
||||
},
|
||||
"workspace_projects": {
|
||||
"label": "{count, plural, one {Progetto} other {Progetti}}",
|
||||
@@ -2463,9 +2461,5 @@
|
||||
"last_edited_by": "Ultima modifica di",
|
||||
"previously_edited_by": "Precedentemente modificato da",
|
||||
"edited_by": "Modificato da"
|
||||
},
|
||||
"self_hosted_maintenance_message": {
|
||||
"plane_didnt_start_up_this_could_be_because_one_or_more_plane_services_failed_to_start": "Plane non si è avviato. Questo potrebbe essere dovuto al fatto che uno o più servizi Plane non sono riusciti ad avviarsi.",
|
||||
"choose_view_logs_from_setup_sh_and_docker_logs_to_be_sure": "Scegli View Logs da setup.sh e dai log Docker per essere sicuro."
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -867,21 +867,7 @@
|
||||
"view": "ビュー",
|
||||
"deactivated_user": "無効化されたユーザー",
|
||||
"apply": "適用",
|
||||
"applying": "適用中",
|
||||
"users": "ユーザー",
|
||||
"admins": "管理者",
|
||||
"guests": "ゲスト",
|
||||
"on_track": "順調",
|
||||
"off_track": "遅れ",
|
||||
"at_risk": "リスクあり",
|
||||
"timeline": "タイムライン",
|
||||
"completion": "完了",
|
||||
"upcoming": "今後の予定",
|
||||
"completed": "完了",
|
||||
"in_progress": "進行中",
|
||||
"planned": "計画済み",
|
||||
"paused": "一時停止",
|
||||
"no_of": "{entity} の数"
|
||||
"applying": "適用中"
|
||||
},
|
||||
"chart": {
|
||||
"x_axis": "エックス アクシス",
|
||||
@@ -1331,6 +1317,19 @@
|
||||
"custom": "カスタムアナリティクス"
|
||||
},
|
||||
"empty_state": {
|
||||
"general": {
|
||||
"title": "進捗、ワークロード、割り当てを追跡。傾向を把握し、ブロッカーを解消して、作業をより速く進めましょう",
|
||||
"description": "スコープと需要、見積もり、スコープクリープを確認できます。チームメンバーとチームのパフォーマンスを把握し、プロジェクトが予定通りに進むようにします。",
|
||||
"primary_button": {
|
||||
"text": "最初のプロジェクトを開始",
|
||||
"comic": {
|
||||
"title": "アナリティクスはサイクル + モジュールで最も効果を発揮",
|
||||
"description": "まず、作業項目をサイクルでタイムボックス化し、可能であれば、複数のサイクルにまたがる作業項目をモジュールにグループ化します。左のナビゲーションで両方を確認してください。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"empty_state_v2": {
|
||||
"customized_insights": {
|
||||
"description": "あなたに割り当てられた作業項目は、ステータスごとに分類されてここに表示されます。",
|
||||
"title": "まだデータがありません"
|
||||
@@ -1346,22 +1345,21 @@
|
||||
},
|
||||
"created_vs_resolved": "作成 vs 解決",
|
||||
"customized_insights": "カスタマイズされたインサイト",
|
||||
"backlog_work_items": "バックログの{entity}",
|
||||
"backlog_work_items": "バックログの作業項目",
|
||||
"active_projects": "アクティブなプロジェクト",
|
||||
"trend_on_charts": "グラフの傾向",
|
||||
"all_projects": "すべてのプロジェクト",
|
||||
"summary_of_projects": "プロジェクトの概要",
|
||||
"project_insights": "プロジェクトのインサイト",
|
||||
"started_work_items": "開始された{entity}",
|
||||
"total_work_items": "{entity}の合計",
|
||||
"started_work_items": "開始された作業項目",
|
||||
"total_work_items": "作業項目の合計",
|
||||
"total_projects": "プロジェクト合計",
|
||||
"total_admins": "管理者の合計",
|
||||
"total_users": "ユーザー総数",
|
||||
"total_intake": "総収入",
|
||||
"un_started_work_items": "未開始の{entity}",
|
||||
"un_started_work_items": "未開始の作業項目",
|
||||
"total_guests": "ゲストの合計",
|
||||
"completed_work_items": "完了した{entity}",
|
||||
"total": "{entity}の合計"
|
||||
"completed_work_items": "完了した作業項目"
|
||||
},
|
||||
"workspace_projects": {
|
||||
"label": "{count, plural, one {プロジェクト} other {プロジェクト}}",
|
||||
@@ -2464,9 +2462,5 @@
|
||||
"last_edited_by": "最終編集者",
|
||||
"previously_edited_by": "以前の編集者",
|
||||
"edited_by": "編集者"
|
||||
},
|
||||
"self_hosted_maintenance_message": {
|
||||
"plane_didnt_start_up_this_could_be_because_one_or_more_plane_services_failed_to_start": "Planeが起動しませんでした。これは1つまたは複数のPlaneサービスの起動に失敗したことが原因である可能性があります。",
|
||||
"choose_view_logs_from_setup_sh_and_docker_logs_to_be_sure": "setup.shとDockerログからView Logsを選択して確認してください。"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -868,21 +868,7 @@
|
||||
"view": "보기",
|
||||
"deactivated_user": "비활성화된 사용자",
|
||||
"apply": "적용",
|
||||
"applying": "적용 중",
|
||||
"users": "사용자",
|
||||
"admins": "관리자",
|
||||
"guests": "게스트",
|
||||
"on_track": "계획대로 진행 중",
|
||||
"off_track": "계획 이탈",
|
||||
"at_risk": "위험",
|
||||
"timeline": "타임라인",
|
||||
"completion": "완료",
|
||||
"upcoming": "예정된",
|
||||
"completed": "완료됨",
|
||||
"in_progress": "진행 중",
|
||||
"planned": "계획된",
|
||||
"paused": "일시 중지됨",
|
||||
"no_of": "{entity} 수"
|
||||
"applying": "적용 중"
|
||||
},
|
||||
"chart": {
|
||||
"x_axis": "X축",
|
||||
@@ -1332,6 +1318,19 @@
|
||||
"custom": "맞춤형 분석"
|
||||
},
|
||||
"empty_state": {
|
||||
"general": {
|
||||
"title": "진행 상황, 작업량 및 할당을 추적하세요. 트렌드를 파악하고, 차단 요소를 제거하며, 작업을 더 빠르게 진행하세요",
|
||||
"description": "범위 대 수요, 추정치 및 범위 크리프를 확인하세요. 팀원과 팀의 성과를 확인하고 프로젝트가 제시간에 진행되도록 하세요.",
|
||||
"primary_button": {
|
||||
"text": "첫 번째 프로젝트 시작",
|
||||
"comic": {
|
||||
"title": "분석은 주기 + 모듈과 함께 작동합니다",
|
||||
"description": "먼저 작업 항목을 주기로 시간 상자화하고, 주기를 초과하는 작업 항목을 모듈로 그룹화하세요. 왼쪽 탐색에서 둘 다 확인하세요."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"empty_state_v2": {
|
||||
"customized_insights": {
|
||||
"description": "귀하에게 할당된 작업 항목이 상태별로 나누어 여기에 표시됩니다.",
|
||||
"title": "아직 데이터가 없습니다"
|
||||
@@ -1347,22 +1346,21 @@
|
||||
},
|
||||
"created_vs_resolved": "생성됨 vs 해결됨",
|
||||
"customized_insights": "맞춤형 인사이트",
|
||||
"backlog_work_items": "백로그 {entity}",
|
||||
"backlog_work_items": "백로그 작업 항목",
|
||||
"active_projects": "활성 프로젝트",
|
||||
"trend_on_charts": "차트의 추세",
|
||||
"all_projects": "모든 프로젝트",
|
||||
"summary_of_projects": "프로젝트 요약",
|
||||
"project_insights": "프로젝트 인사이트",
|
||||
"started_work_items": "시작된 {entity}",
|
||||
"total_work_items": "총 {entity}",
|
||||
"started_work_items": "시작된 작업 항목",
|
||||
"total_work_items": "총 작업 항목",
|
||||
"total_projects": "총 프로젝트 수",
|
||||
"total_admins": "총 관리자 수",
|
||||
"total_users": "총 사용자 수",
|
||||
"total_intake": "총 수입",
|
||||
"un_started_work_items": "시작되지 않은 {entity}",
|
||||
"un_started_work_items": "시작되지 않은 작업 항목",
|
||||
"total_guests": "총 게스트 수",
|
||||
"completed_work_items": "완료된 {entity}",
|
||||
"total": "총 {entity}"
|
||||
"completed_work_items": "완료된 작업 항목"
|
||||
},
|
||||
"workspace_projects": {
|
||||
"label": "{count, plural, one {프로젝트} other {프로젝트}}",
|
||||
@@ -2466,9 +2464,5 @@
|
||||
"last_edited_by": "마지막 편집자",
|
||||
"previously_edited_by": "이전 편집자",
|
||||
"edited_by": "편집자"
|
||||
},
|
||||
"self_hosted_maintenance_message": {
|
||||
"plane_didnt_start_up_this_could_be_because_one_or_more_plane_services_failed_to_start": "Plane이 시작되지 않았습니다. 이는 하나 이상의 Plane 서비스가 시작에 실패했기 때문일 수 있습니다.",
|
||||
"choose_view_logs_from_setup_sh_and_docker_logs_to_be_sure": "확실히 하려면 setup.sh와 Docker 로그에서 View Logs를 선택하세요."
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -868,21 +868,7 @@
|
||||
"view": "Widok",
|
||||
"deactivated_user": "Dezaktywowany użytkownik",
|
||||
"apply": "Zastosuj",
|
||||
"applying": "Zastosowanie",
|
||||
"users": "Użytkownicy",
|
||||
"admins": "Administratorzy",
|
||||
"guests": "Goście",
|
||||
"on_track": "Na dobrej drodze",
|
||||
"off_track": "Poza planem",
|
||||
"at_risk": "W zagrożeniu",
|
||||
"timeline": "Oś czasu",
|
||||
"completion": "Zakończenie",
|
||||
"upcoming": "Nadchodzące",
|
||||
"completed": "Zakończone",
|
||||
"in_progress": "W trakcie",
|
||||
"planned": "Zaplanowane",
|
||||
"paused": "Wstrzymane",
|
||||
"no_of": "Liczba {entity}"
|
||||
"applying": "Zastosowanie"
|
||||
},
|
||||
"chart": {
|
||||
"x_axis": "Oś X",
|
||||
@@ -1332,6 +1318,19 @@
|
||||
"custom": "Analizy niestandardowe"
|
||||
},
|
||||
"empty_state": {
|
||||
"general": {
|
||||
"title": "Śledź postępy, obciążenie i alokacje. Identyfikuj trendy, usuwaj przeszkody i przyspieszaj pracę",
|
||||
"description": "Obserwuj zakres vs. zapotrzebowanie, szacunki i zakres. Sprawdzaj wydajność członków i zespołów, upewnij się, że projekty kończą się na czas.",
|
||||
"primary_button": {
|
||||
"text": "Zacznij pierwszy projekt",
|
||||
"comic": {
|
||||
"title": "Analizy najlepiej działają z Cyklem + Modułami",
|
||||
"description": "Najpierw ogranicz pracę w cyklach i grupuj zadania w modułach obejmujących wiele cykli. Znajdziesz je w menu po lewej."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"empty_state_v2": {
|
||||
"customized_insights": {
|
||||
"description": "Przypisane do Ciebie elementy pracy, podzielone według stanu, pojawią się tutaj.",
|
||||
"title": "Brak danych"
|
||||
@@ -1347,22 +1346,21 @@
|
||||
},
|
||||
"created_vs_resolved": "Utworzone vs Rozwiązane",
|
||||
"customized_insights": "Dostosowane informacje",
|
||||
"backlog_work_items": "{entity} w backlogu",
|
||||
"backlog_work_items": "Elementy pracy w backlogu",
|
||||
"active_projects": "Aktywne projekty",
|
||||
"trend_on_charts": "Trend na wykresach",
|
||||
"all_projects": "Wszystkie projekty",
|
||||
"summary_of_projects": "Podsumowanie projektów",
|
||||
"project_insights": "Wgląd w projekt",
|
||||
"started_work_items": "Rozpoczęte {entity}",
|
||||
"total_work_items": "Łączna liczba {entity}",
|
||||
"started_work_items": "Rozpoczęte elementy pracy",
|
||||
"total_work_items": "Łączna liczba elementów pracy",
|
||||
"total_projects": "Łączna liczba projektów",
|
||||
"total_admins": "Łączna liczba administratorów",
|
||||
"total_users": "Łączna liczba użytkowników",
|
||||
"total_intake": "Całkowity dochód",
|
||||
"un_started_work_items": "Nierozpoczęte {entity}",
|
||||
"un_started_work_items": "Nierozpoczęte elementy pracy",
|
||||
"total_guests": "Łączna liczba gości",
|
||||
"completed_work_items": "Ukończone {entity}",
|
||||
"total": "Łączna liczba {entity}"
|
||||
"completed_work_items": "Ukończone elementy pracy"
|
||||
},
|
||||
"workspace_projects": {
|
||||
"label": "{count, plural, one {Projekt} few {Projekty} other {Projektów}}",
|
||||
@@ -2465,9 +2463,5 @@
|
||||
"last_edited_by": "Ostatnio edytowane przez",
|
||||
"previously_edited_by": "Wcześniej edytowane przez",
|
||||
"edited_by": "Edytowane przez"
|
||||
},
|
||||
"self_hosted_maintenance_message": {
|
||||
"plane_didnt_start_up_this_could_be_because_one_or_more_plane_services_failed_to_start": "Plane nie uruchomił się. Może to być spowodowane tym, że jedna lub więcej usług Plane nie mogła się uruchomić.",
|
||||
"choose_view_logs_from_setup_sh_and_docker_logs_to_be_sure": "Wybierz View Logs z setup.sh i logów Docker, aby mieć pewność."
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -868,21 +868,7 @@
|
||||
"view": "Visualizar",
|
||||
"deactivated_user": "Usuário desativado",
|
||||
"apply": "Aplicar",
|
||||
"applying": "Aplicando",
|
||||
"users": "Usuários",
|
||||
"admins": "Administradores",
|
||||
"guests": "Convidados",
|
||||
"on_track": "No caminho certo",
|
||||
"off_track": "Fora do caminho",
|
||||
"at_risk": "Em risco",
|
||||
"timeline": "Linha do tempo",
|
||||
"completion": "Conclusão",
|
||||
"upcoming": "Próximo",
|
||||
"completed": "Concluído",
|
||||
"in_progress": "Em andamento",
|
||||
"planned": "Planejado",
|
||||
"paused": "Pausado",
|
||||
"no_of": "Nº de {entity}"
|
||||
"applying": "Aplicando"
|
||||
},
|
||||
"chart": {
|
||||
"x_axis": "Eixo X",
|
||||
@@ -1332,6 +1318,19 @@
|
||||
"custom": "Análises Personalizadas"
|
||||
},
|
||||
"empty_state": {
|
||||
"general": {
|
||||
"title": "Acompanhe o progresso, as cargas de trabalho e as alocações. Identifique tendências, remova bloqueadores e mova o trabalho mais rapidamente",
|
||||
"description": "Veja o escopo versus a demanda, as estimativas e o aumento do escopo. Obtenha o desempenho por membros da equipe e equipes, e certifique-se de que seu projeto seja executado no prazo.",
|
||||
"primary_button": {
|
||||
"text": "Comece seu primeiro projeto",
|
||||
"comic": {
|
||||
"title": "A análise funciona melhor com Ciclos + Módulos",
|
||||
"description": "Primeiro, coloque seus itens de trabalho em Ciclos e, se puder, agrupe os itens de trabalho que abrangem mais de um ciclo em Módulos. Confira ambos na navegação à esquerda."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"empty_state_v2": {
|
||||
"customized_insights": {
|
||||
"description": "Os itens de trabalho atribuídos a você, divididos por estado, aparecerão aqui.",
|
||||
"title": "Ainda não há dados"
|
||||
@@ -1347,22 +1346,21 @@
|
||||
},
|
||||
"created_vs_resolved": "Criado vs Resolvido",
|
||||
"customized_insights": "Insights personalizados",
|
||||
"backlog_work_items": "{entity} no backlog",
|
||||
"backlog_work_items": "Itens de trabalho no backlog",
|
||||
"active_projects": "Projetos ativos",
|
||||
"trend_on_charts": "Tendência nos gráficos",
|
||||
"all_projects": "Todos os projetos",
|
||||
"summary_of_projects": "Resumo dos projetos",
|
||||
"project_insights": "Insights do projeto",
|
||||
"started_work_items": "{entity} iniciados",
|
||||
"total_work_items": "Total de {entity}",
|
||||
"started_work_items": "Itens de trabalho iniciados",
|
||||
"total_work_items": "Total de itens de trabalho",
|
||||
"total_projects": "Total de projetos",
|
||||
"total_admins": "Total de administradores",
|
||||
"total_users": "Total de usuários",
|
||||
"total_intake": "Receita total",
|
||||
"un_started_work_items": "{entity} não iniciados",
|
||||
"un_started_work_items": "Itens de trabalho não iniciados",
|
||||
"total_guests": "Total de convidados",
|
||||
"completed_work_items": "{entity} concluídos",
|
||||
"total": "Total de {entity}"
|
||||
"completed_work_items": "Itens de trabalho concluídos"
|
||||
},
|
||||
"workspace_projects": {
|
||||
"label": "{count, plural, one {Projeto} other {Projetos}}",
|
||||
@@ -2460,9 +2458,5 @@
|
||||
"last_edited_by": "Última edição por",
|
||||
"previously_edited_by": "Anteriormente editado por",
|
||||
"edited_by": "Editado por"
|
||||
},
|
||||
"self_hosted_maintenance_message": {
|
||||
"plane_didnt_start_up_this_could_be_because_one_or_more_plane_services_failed_to_start": "O Plane não inicializou. Isso pode ser porque um ou mais serviços do Plane falharam ao iniciar.",
|
||||
"choose_view_logs_from_setup_sh_and_docker_logs_to_be_sure": "Escolha View Logs do setup.sh e logs do Docker para ter certeza."
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user