Initial commit: Plane
Some checks failed
Branch Build CE / Build Setup (push) Has been cancelled
Branch Build CE / Build-Push Admin Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Web Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Space Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Live Collaboration Docker Image (push) Has been cancelled
Branch Build CE / Build-Push API Server Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Proxy Docker Image (push) Has been cancelled
Branch Build CE / Build-Push AIO Docker Image (push) Has been cancelled
Branch Build CE / Upload Build Assets (push) Has been cancelled
Branch Build CE / Build Release (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Codespell / Check for spelling errors (push) Has been cancelled
Sync Repositories / sync_changes (push) Has been cancelled

Synced from upstream: 8853637e981ed7d8a6cff32bd98e7afe20f54362
This commit is contained in:
chuan
2025-11-07 00:00:52 +08:00
commit 8ebde8aa05
4886 changed files with 462270 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
node_modules
build/*
dist/*
out/*

View File

@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: ["@plane/eslint-config/library.js"],
};

View File

@@ -0,0 +1,5 @@
.next
.turbo
out/
dist/
build/

View File

@@ -0,0 +1,5 @@
{
"printWidth": 120,
"tabWidth": 2,
"trailingComma": "es5"
}

View File

@@ -0,0 +1,37 @@
{
"name": "@plane/constants",
"version": "1.1.0",
"private": true,
"license": "AGPL-3.0",
"scripts": {
"dev": "tsdown --watch",
"build": "tsdown",
"check:lint": "eslint . --max-warnings 0",
"check:types": "tsc --noEmit",
"check:format": "prettier --check \"**/*.{ts,tsx,md,json,css,scss}\"",
"fix:lint": "eslint . --fix",
"fix:format": "prettier --write \"**/*.{ts,tsx,md,json,css,scss}\"",
"clean": "rm -rf .turbo && rm -rf .next && rm -rf node_modules && rm -rf dist"
},
"dependencies": {
"@plane/types": "workspace:*"
},
"devDependencies": {
"@plane/eslint-config": "workspace:*",
"@plane/typescript-config": "workspace:*",
"@types/node": "catalog:",
"@types/react": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:"
},
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./package.json": "./package.json"
}
}

View File

@@ -0,0 +1,3 @@
export enum AI_EDITOR_TASKS {
ASK_ANYTHING = "ASK_ANYTHING",
}

View File

@@ -0,0 +1,183 @@
import { ChartXAxisProperty, ChartYAxisMetric, TAnalyticsTabsBase } from "@plane/types";
export interface IInsightField {
key: string;
i18nKey: string;
i18nProps?: {
entity?: string;
entityPlural?: string;
prefix?: string;
suffix?: string;
[key: string]: unknown;
};
}
export const ANALYTICS_INSIGHTS_FIELDS: 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",
},
{
value: ChartYAxisMetric.EPIC_WORK_ITEM_COUNT,
label: "Epic",
},
];
export const ANALYTICS_V2_DATE_KEYS = ["completed_at", "target_date", "start_date", "created_at"];

View File

@@ -0,0 +1 @@
export * from "./common";

View File

@@ -0,0 +1,158 @@
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;
export const SPACE_PASSWORD_CRITERIA = [
{
key: "min_8_char",
label: "Min 8 characters",
isCriteriaValid: (password: string) => password.length >= PASSWORD_MIN_LENGTH,
},
// {
// key: "min_1_upper_case",
// label: "Min 1 upper-case letter",
// isCriteriaValid: (password: string) => PASSWORD_NUMBER_REGEX.test(password),
// },
// {
// key: "min_1_number",
// label: "Min 1 number",
// isCriteriaValid: (password: string) => PASSWORD_CHAR_CAPS_REGEX.test(password),
// },
// {
// key: "min_1_special_char",
// label: "Min 1 special character",
// isCriteriaValid: (password: string) => PASSWORD_SPECIAL_CHAR_REGEX.test(password),
// },
];
export enum EAuthPageTypes {
PUBLIC = "PUBLIC",
NON_AUTHENTICATED = "NON_AUTHENTICATED",
SET_PASSWORD = "SET_PASSWORD",
ONBOARDING = "ONBOARDING",
AUTHENTICATED = "AUTHENTICATED",
}
export enum EPageTypes {
INIT = "INIT",
PUBLIC = "PUBLIC",
NON_AUTHENTICATED = "NON_AUTHENTICATED",
ONBOARDING = "ONBOARDING",
AUTHENTICATED = "AUTHENTICATED",
}
export enum EAuthModes {
SIGN_IN = "SIGN_IN",
SIGN_UP = "SIGN_UP",
}
export enum EAuthSteps {
EMAIL = "EMAIL",
PASSWORD = "PASSWORD",
UNIQUE_CODE = "UNIQUE_CODE",
}
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;
code: EAuthErrorCodes;
title: string;
message: string | React.ReactNode;
};
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",
}
export type TAdminAuthErrorInfo = {
type: EErrorAlertType;
code: EAdminAuthErrorCodes;
title: string;
message: string | React.ReactNode;
};
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",
// Password strength
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",
// 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",
// 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",
// 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",
// Reset Password
INVALID_PASSWORD_TOKEN = "5125",
EXPIRED_PASSWORD_TOKEN = "5130",
// Change password
INCORRECT_OLD_PASSWORD = "5135",
MISSING_PASSWORD = "5138",
INVALID_NEW_PASSWORD = "5140",
// set password
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",
// Rate limit
RATE_LIMIT_EXCEEDED = "5900",
}

View File

@@ -0,0 +1,124 @@
import { ChartXAxisProperty, TChartColorScheme } from "@plane/types";
export const LABEL_CLASSNAME = "uppercase text-custom-text-300/60 text-sm tracking-wide";
export const AXIS_LABEL_CLASSNAME = "uppercase text-custom-text-300/60 text-sm tracking-wide";
export enum ChartXAxisDateGrouping {
DAY = "DAY",
WEEK = "WEEK",
MONTH = "MONTH",
YEAR = "YEAR",
}
export const TO_CAPITALIZE_PROPERTIES: ChartXAxisProperty[] = [
ChartXAxisProperty.PRIORITY,
ChartXAxisProperty.STATE_GROUPS,
];
export const CHART_X_AXIS_DATE_PROPERTIES: ChartXAxisProperty[] = [
ChartXAxisProperty.START_DATE,
ChartXAxisProperty.TARGET_DATE,
ChartXAxisProperty.CREATED_AT,
ChartXAxisProperty.COMPLETED_AT,
];
export enum EChartModels {
BASIC = "BASIC",
STACKED = "STACKED",
GROUPED = "GROUPED",
MULTI_LINE = "MULTI_LINE",
COMPARISON = "COMPARISON",
PROGRESS = "PROGRESS",
}
export const CHART_COLOR_PALETTES: {
key: TChartColorScheme;
i18n_label: string;
light: string[];
dark: string[];
}[] = [
{
key: "modern",
i18n_label: "dashboards.widget.color_palettes.modern",
light: [
"#6172E8",
"#8B6EDB",
"#E05F99",
"#29A383",
"#CB8A37",
"#3AA7C1",
"#F1B24A",
"#E84855",
"#50C799",
"#B35F9E",
],
dark: [
"#6B7CDE",
"#8E9DE6",
"#D45D9E",
"#2EAF85",
"#D4A246",
"#29A7C1",
"#B89F6A",
"#D15D64",
"#4ED079",
"#A169A4",
],
},
{
key: "horizon",
i18n_label: "dashboards.widget.color_palettes.horizon",
light: [
"#E76E50",
"#289D90",
"#F3A362",
"#E9C368",
"#264753",
"#8A6FA0",
"#5B9EE5",
"#7CC474",
"#BA7DB5",
"#CF8640",
],
dark: [
"#E05A3A",
"#1D8A7E",
"#D98B4D",
"#D1AC50",
"#3A6B7C",
"#7D6297",
"#4D8ACD",
"#569C64",
"#C16A8C",
"#B77436",
],
},
{
key: "earthen",
i18n_label: "dashboards.widget.color_palettes.earthen",
light: [
"#386641",
"#6A994E",
"#A7C957",
"#E97F4E",
"#BC4749",
"#9E2A2B",
"#80CED1",
"#5C3E79",
"#526EAB",
"#6B5B95",
],
dark: [
"#497752",
"#7BAA5F",
"#B8DA68",
"#FA905F",
"#CD585A",
"#AF3B3C",
"#91DFE2",
"#6D4F8A",
"#637FBC",
"#7C6CA6",
],
},
];

View File

@@ -0,0 +1,42 @@
// types
export const CYCLE_STATUS: {
i18n_label: string;
value: "current" | "upcoming" | "completed" | "draft";
i18n_title: string;
color: string;
textColor: string;
bgColor: string;
}[] = [
{
i18n_label: "project_cycles.status.days_left",
value: "current",
i18n_title: "project_cycles.status.in_progress",
color: "#F59E0B",
textColor: "text-amber-500",
bgColor: "bg-amber-50",
},
{
i18n_label: "project_cycles.status.yet_to_start",
value: "upcoming",
i18n_title: "project_cycles.status.yet_to_start",
color: "#3F76FF",
textColor: "text-blue-500",
bgColor: "bg-indigo-50",
},
{
i18n_label: "project_cycles.status.completed",
value: "completed",
i18n_title: "project_cycles.status.completed",
color: "#16A34A",
textColor: "text-green-600",
bgColor: "bg-green-50",
},
{
i18n_label: "project_cycles.status.draft",
value: "draft",
i18n_title: "project_cycles.status.draft",
color: "#525252",
textColor: "text-custom-text-300",
bgColor: "bg-custom-background-90",
},
];

View File

@@ -0,0 +1,92 @@
// types
import { TIssuesListTypes } from "@plane/types";
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: {
key: EDurationFilters;
label: string;
}[] = [
{
key: EDurationFilters.NONE,
label: "All time",
},
{
key: EDurationFilters.TODAY,
label: "Due today",
},
{
key: EDurationFilters.THIS_WEEK,
label: "Due this week",
},
{
key: EDurationFilters.THIS_MONTH,
label: "Due this month",
},
{
key: EDurationFilters.THIS_YEAR,
label: "Due this year",
},
{
key: EDurationFilters.CUSTOM,
label: "Custom",
},
];
// random background colors for project cards
export const PROJECT_BACKGROUND_COLORS = [
"bg-gray-500/20",
"bg-green-500/20",
"bg-red-500/20",
"bg-orange-500/20",
"bg-blue-500/20",
"bg-yellow-500/20",
"bg-pink-500/20",
"bg-purple-500/20",
];
// assigned and created issues widgets tabs list
export const FILTERED_ISSUES_TABS_LIST: {
key: TIssuesListTypes;
label: string;
}[] = [
{
key: "upcoming",
label: "Upcoming",
},
{
key: "overdue",
label: "Overdue",
},
{
key: "completed",
label: "Marked completed",
},
];
// assigned and created issues widgets tabs list
export const UNFILTERED_ISSUES_TABS_LIST: {
key: TIssuesListTypes;
label: string;
}[] = [
{
key: "pending",
label: "Pending",
},
{
key: "completed",
label: "Marked completed",
},
];
export type TLinkOptions = {
userId: string | undefined;
};

View File

@@ -0,0 +1,25 @@
export const ISSUE_REACTION_EMOJI_CODES = [
"128077",
"128078",
"128516",
"128165",
"128533",
"129505",
"9992",
"128064",
];
export const RANDOM_EMOJI_CODES = [
"8986",
"9200",
"128204",
"127773",
"127891",
"128076",
"128077",
"128187",
"128188",
"128512",
"128522",
"128578",
];

View File

@@ -0,0 +1,27 @@
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "";
export const API_BASE_PATH = process.env.NEXT_PUBLIC_API_BASE_PATH || "";
export const API_URL = encodeURI(`${API_BASE_URL}${API_BASE_PATH}`);
// God Mode Admin App Base Url
export const ADMIN_BASE_URL = process.env.NEXT_PUBLIC_ADMIN_BASE_URL || "";
export const ADMIN_BASE_PATH = process.env.NEXT_PUBLIC_ADMIN_BASE_PATH || "";
export const GOD_MODE_URL = encodeURI(`${ADMIN_BASE_URL}${ADMIN_BASE_PATH}`);
// Publish App Base Url
export const SPACE_BASE_URL = process.env.NEXT_PUBLIC_SPACE_BASE_URL || "";
export const SPACE_BASE_PATH = process.env.NEXT_PUBLIC_SPACE_BASE_PATH || "";
export const SITES_URL = encodeURI(`${SPACE_BASE_URL}${SPACE_BASE_PATH}`);
// Live App Base Url
export const LIVE_BASE_URL = process.env.NEXT_PUBLIC_LIVE_BASE_URL || "";
export const LIVE_BASE_PATH = process.env.NEXT_PUBLIC_LIVE_BASE_PATH || "";
export const LIVE_URL = encodeURI(`${LIVE_BASE_URL}${LIVE_BASE_PATH}`);
// Web App Base Url
export const WEB_BASE_URL = process.env.NEXT_PUBLIC_WEB_BASE_URL || "";
export const WEB_BASE_PATH = process.env.NEXT_PUBLIC_WEB_BASE_PATH || "";
export const WEB_URL = encodeURI(`${WEB_BASE_URL}${WEB_BASE_PATH}`);
// plane website url
export const WEBSITE_URL = process.env.NEXT_PUBLIC_WEBSITE_URL || "https://plane.so";
// support email
export const SUPPORT_EMAIL = process.env.NEXT_PUBLIC_SUPPORT_EMAIL || "support@plane.so";
// marketing links
export const MARKETING_PRICING_PAGE_LINK = "https://plane.so/pricing";
export const MARKETING_CONTACT_US_PAGE_LINK = "https://plane.so/contact";
export const MARKETING_PLANE_ONE_PAGE_LINK = "https://plane.so/one";

View File

@@ -0,0 +1,136 @@
// plane imports
import { TEstimateSystems } from "@plane/types";
export const MAX_ESTIMATE_POINT_INPUT_LENGTH = 20;
export enum EEstimateSystem {
POINTS = "points",
CATEGORIES = "categories",
TIME = "time",
}
export enum EEstimateUpdateStages {
CREATE = "create",
EDIT = "edit",
SWITCH = "switch",
}
export const estimateCount = {
min: 2,
max: 6,
};
export const ESTIMATE_SYSTEMS: TEstimateSystems = {
points: {
name: "Points",
i18n_name: "project_settings.estimates.systems.points.label",
templates: {
fibonacci: {
title: "Fibonacci",
i18n_title: "project_settings.estimates.systems.points.fibonacci",
values: [
{ id: undefined, key: 1, value: "1" },
{ id: undefined, key: 2, value: "2" },
{ id: undefined, key: 3, value: "3" },
{ id: undefined, key: 4, value: "5" },
{ id: undefined, key: 5, value: "8" },
{ id: undefined, key: 6, value: "13" },
],
},
linear: {
title: "Linear",
i18n_title: "project_settings.estimates.systems.points.linear",
values: [
{ id: undefined, key: 1, value: "1" },
{ id: undefined, key: 2, value: "2" },
{ id: undefined, key: 3, value: "3" },
{ id: undefined, key: 4, value: "4" },
{ id: undefined, key: 5, value: "5" },
{ id: undefined, key: 6, value: "6" },
],
},
squares: {
title: "Squares",
i18n_title: "project_settings.estimates.systems.points.squares",
values: [
{ id: undefined, key: 1, value: "1" },
{ id: undefined, key: 2, value: "4" },
{ id: undefined, key: 3, value: "9" },
{ id: undefined, key: 4, value: "16" },
{ id: undefined, key: 5, value: "25" },
{ id: undefined, key: 6, value: "36" },
],
},
custom: {
title: "Custom",
i18n_title: "project_settings.estimates.systems.points.custom",
values: [
{ id: undefined, key: 1, value: "1" },
{ id: undefined, key: 2, value: "2" },
],
hide: true,
},
},
is_available: true,
is_ee: false,
},
categories: {
name: "Categories",
i18n_name: "project_settings.estimates.systems.categories.label",
templates: {
t_shirt_sizes: {
title: "T-Shirt Sizes",
i18n_title: "project_settings.estimates.systems.categories.t_shirt_sizes",
values: [
{ id: undefined, key: 1, value: "XS" },
{ id: undefined, key: 2, value: "S" },
{ id: undefined, key: 3, value: "M" },
{ id: undefined, key: 4, value: "L" },
{ id: undefined, key: 5, value: "XL" },
{ id: undefined, key: 6, value: "XXL" },
],
},
easy_to_hard: {
title: "Easy to hard",
i18n_title: "project_settings.estimates.systems.categories.easy_to_hard",
values: [
{ id: undefined, key: 1, value: "Easy" },
{ id: undefined, key: 2, value: "Medium" },
{ id: undefined, key: 3, value: "Hard" },
{ id: undefined, key: 4, value: "Very Hard" },
],
},
custom: {
title: "Custom",
i18n_title: "project_settings.estimates.systems.categories.custom",
values: [
{ id: undefined, key: 1, value: "Easy" },
{ id: undefined, key: 2, value: "Hard" },
],
hide: true,
},
},
is_available: true,
is_ee: false,
},
time: {
name: "Time",
i18n_name: "project_settings.estimates.systems.time.label",
templates: {
hours: {
title: "Hours",
i18n_title: "project_settings.estimates.systems.time.hours",
values: [
{ id: undefined, key: 1, value: "1" },
{ id: undefined, key: 2, value: "2" },
{ id: undefined, key: 3, value: "3" },
{ id: undefined, key: 4, value: "4" },
{ id: undefined, key: 5, value: "5" },
{ id: undefined, key: 6, value: "6" },
],
},
},
is_available: true,
is_ee: true,
},
};

View File

@@ -0,0 +1,502 @@
import { EProductSubscriptionEnum } from "@plane/types";
/**
* ===========================================================================
* Event Groups
* ===========================================================================
*/
export const GROUP_WORKSPACE_TRACKER_EVENT = "workspace_metrics";
export const GITHUB_REDIRECTED_TRACKER_EVENT = "github_redirected";
export const HEADER_GITHUB_ICON = "header_github_icon";
/**
* ===========================================================================
* Command palette tracker
* ===========================================================================
*/
export const COMMAND_PALETTE_TRACKER_ELEMENTS = {
COMMAND_PALETTE_SHORTCUT_KEY: "command_palette_shortcut_key",
};
/**
* ===========================================================================
* Workspace Events and Elements
* ===========================================================================
*/
export const WORKSPACE_TRACKER_EVENTS = {
create: "workspace_created",
update: "workspace_updated",
delete: "workspace_deleted",
};
export const WORKSPACE_TRACKER_ELEMENTS = {
DELETE_WORKSPACE_BUTTON: "delete_workspace_button",
ONBOARDING_CREATE_WORKSPACE_BUTTON: "onboarding_create_workspace_button",
CREATE_WORKSPACE_BUTTON: "create_workspace_button",
UPDATE_WORKSPACE_BUTTON: "update_workspace_button",
};
/**
* ===========================================================================
* Project Events and Elements
* ===========================================================================
*/
export const PROJECT_TRACKER_EVENTS = {
create: "project_created",
update: "project_updated",
delete: "project_deleted",
feature_toggled: "feature_toggled",
};
export const PROJECT_TRACKER_ELEMENTS = {
EXTENDED_SIDEBAR_ADD_BUTTON: "extended_sidebar_add_project_button",
SIDEBAR_CREATE_PROJECT_BUTTON: "sidebar_create_project_button",
SIDEBAR_CREATE_PROJECT_TOOLTIP: "sidebar_create_project_tooltip",
COMMAND_PALETTE_CREATE_BUTTON: "command_palette_create_project_button",
COMMAND_PALETTE_SHORTCUT_CREATE_BUTTON: "command_palette_shortcut_create_project_button",
EMPTY_STATE_CREATE_PROJECT_BUTTON: "empty_state_create_project_button",
CREATE_HEADER_BUTTON: "create_project_header_button",
CREATE_FIRST_PROJECT_BUTTON: "create_first_project_button",
DELETE_PROJECT_BUTTON: "delete_project_button",
UPDATE_PROJECT_BUTTON: "update_project_button",
CREATE_PROJECT_JIRA_IMPORT_DETAIL_PAGE: "create_project_jira_import_detail_page",
TOGGLE_FEATURE: "toggle_project_feature",
};
/**
* ===========================================================================
* Cycle Events and Elements
* ===========================================================================
*/
export const CYCLE_TRACKER_EVENTS = {
create: "cycle_created",
update: "cycle_updated",
delete: "cycle_deleted",
favorite: "cycle_favorited",
unfavorite: "cycle_unfavorited",
archive: "cycle_archived",
restore: "cycle_restored",
};
export const CYCLE_TRACKER_ELEMENTS = {
RIGHT_HEADER_ADD_BUTTON: "right_header_add_cycle_button",
EMPTY_STATE_ADD_BUTTON: "empty_state_add_cycle_button",
COMMAND_PALETTE_ADD_ITEM: "command_palette_add_cycle_item",
RIGHT_SIDEBAR: "cycle_right_sidebar",
QUICK_ACTIONS: "cycle_quick_actions",
CONTEXT_MENU: "cycle_context_menu",
LIST_ITEM: "cycle_list_item",
} as const;
/**
* ===========================================================================
* Module Events and Elements
* ===========================================================================
*/
export const MODULE_TRACKER_EVENTS = {
create: "module_created",
update: "module_updated",
delete: "module_deleted",
favorite: "module_favorited",
unfavorite: "module_unfavorited",
archive: "module_archived",
restore: "module_restored",
link: {
create: "module_link_created",
update: "module_link_updated",
delete: "module_link_deleted",
},
};
export const MODULE_TRACKER_ELEMENTS = {
RIGHT_HEADER_ADD_BUTTON: "right_header_add_module_button",
EMPTY_STATE_ADD_BUTTON: "empty_state_add_module_button",
COMMAND_PALETTE_ADD_ITEM: "command_palette_add_module_item",
RIGHT_SIDEBAR: "module_right_sidebar",
QUICK_ACTIONS: "module_quick_actions",
CONTEXT_MENU: "module_context_menu",
LIST_ITEM: "module_list_item",
CARD_ITEM: "module_card_item",
} as const;
/**
* ===========================================================================
* Work Item Events and Elements
* ===========================================================================
*/
export const WORK_ITEM_TRACKER_EVENTS = {
create: "work_item_created",
add_existing: "work_item_add_existing",
update: "work_item_updated",
delete: "work_item_deleted",
archive: "work_item_archived",
restore: "work_item_restored",
attachment: {
add: "work_item_attachment_added",
remove: "work_item_attachment_removed",
},
sub_issue: {
update: "sub_issue_updated",
remove: "sub_issue_removed",
delete: "sub_issue_deleted",
create: "sub_issue_created",
add_existing: "sub_issue_add_existing",
},
draft: {
create: "draft_work_item_created",
},
};
export const WORK_ITEM_TRACKER_ELEMENTS = {
HEADER_ADD_BUTTON: {
WORK_ITEMS: "work_items_header_add_work_item_button",
PROJECT_VIEW: "project_view_header_add_work_item_button",
CYCLE: "cycle_header_add_work_item_button",
MODULE: "module_header_add_work_item_button",
},
COMMAND_PALETTE_ADD_BUTTON: "command_palette_add_work_item_button",
EMPTY_STATE_ADD_BUTTON: {
WORK_ITEMS: "work_items_empty_state_add_work_item_button",
PROJECT_VIEW: "project_view_empty_state_add_work_item_button",
CYCLE: "cycle_empty_state_add_work_item_button",
MODULE: "module_empty_state_add_work_item_button",
GLOBAL_VIEW: "global_view_empty_state_add_work_item_button",
},
QUICK_ACTIONS: {
WORK_ITEMS: "work_items_quick_actions",
PROJECT_VIEW: "project_view_work_items_quick_actions",
CYCLE: "cycle_work_items_quick_actions",
MODULE: "module_work_items_quick_actions",
GLOBAL_VIEW: "global_view_work_items_quick_actions",
ARCHIVED: "archived_work_items_quick_actions",
DRAFT: "draft_work_items_quick_actions",
},
CONTEXT_MENU: {
WORK_ITEMS: "work_items_context_menu",
PROJECT_VIEW: "project_view_context_menu",
CYCLE: "cycle_context_menu",
MODULE: "module_context_menu",
GLOBAL_VIEW: "global_view_context_menu",
ARCHIVED: "archived_context_menu",
DRAFT: "draft_context_menu",
},
} as const;
/**
* ===========================================================================
* State Events and Elements
* ===========================================================================
*/
export const STATE_TRACKER_EVENTS = {
create: "state_created",
update: "state_updated",
delete: "state_deleted",
};
export const STATE_TRACKER_ELEMENTS = {
STATE_GROUP_ADD_BUTTON: "state_group_add_button",
STATE_LIST_DELETE_BUTTON: "state_list_delete_button",
STATE_LIST_EDIT_BUTTON: "state_list_edit_button",
};
/**
* ===========================================================================
* Project Page Events and Elements
* ===========================================================================
*/
export const PROJECT_PAGE_TRACKER_EVENTS = {
create: "project_page_created",
update: "project_page_updated",
delete: "project_page_deleted",
archive: "project_page_archived",
restore: "project_page_restored",
lock: "project_page_locked",
unlock: "project_page_unlocked",
access_update: "project_page_access_updated",
duplicate: "project_page_duplicated",
favorite: "project_page_favorited",
unfavorite: "project_page_unfavorited",
move: "project_page_moved",
};
export const PROJECT_PAGE_TRACKER_ELEMENTS = {
COMMAND_PALETTE_SHORTCUT_CREATE_BUTTON: "command_palette_shortcut_create_page_button",
EMPTY_STATE_CREATE_BUTTON: "empty_state_create_page_button",
COMMAND_PALETTE_CREATE_BUTTON: "command_palette_create_page_button",
CONTEXT_MENU: "page_context_menu",
QUICK_ACTIONS: "page_quick_actions",
LIST_ITEM: "page_list_item",
FAVORITE_BUTTON: "page_favorite_button",
ARCHIVE_BUTTON: "page_archive_button",
LOCK_BUTTON: "page_lock_button",
ACCESS_TOGGLE: "page_access_toggle",
DUPLICATE_BUTTON: "page_duplicate_button",
} as const;
/**
* ===========================================================================
* Member Events and Elements
* ===========================================================================
*/
export const MEMBER_TRACKER_EVENTS = {
invite: "member_invited",
accept: "member_accepted",
project: {
add: "project_member_added",
leave: "project_member_left",
},
workspace: {
leave: "workspace_member_left",
},
};
export const MEMBER_TRACKER_ELEMENTS = {
HEADER_ADD_BUTTON: "header_add_member_button",
ACCEPT_INVITATION_BUTTON: "accept_invitation_button",
ONBOARDING_JOIN_WORKSPACE: "workspace_join_continue_to_workspace_button",
ONBOARDING_INVITE_MEMBER: "invite_member_continue_button",
SIDEBAR_PROJECT_QUICK_ACTIONS: "sidebar_project_quick_actions",
PROJECT_MEMBER_TABLE_CONTEXT_MENU: "project_member_table_context_menu",
WORKSPACE_MEMBER_TABLE_CONTEXT_MENU: "workspace_member_table_context_menu",
WORKSPACE_INVITATIONS_LIST_CONTEXT_MENU: "workspace_invitations_list_context_menu",
} as const;
/**
* ===========================================================================
* Auth Events and Elements
* ===========================================================================
*/
export const AUTH_TRACKER_EVENTS = {
code_verify: "code_verified",
sign_up_with_password: "sign_up_with_password",
sign_in_with_password: "sign_in_with_password",
forgot_password: "forgot_password_clicked",
new_code_requested: "new_code_requested",
password_created: "password_created",
};
export const AUTH_TRACKER_ELEMENTS = {
NAVIGATE_TO_SIGN_UP: "navigate_to_sign_up",
FORGOT_PASSWORD_FROM_SIGNIN: "forgot_password_from_signin",
SIGNUP_FROM_FORGOT_PASSWORD: "signup_from_forgot_password",
SIGN_IN_FROM_SIGNUP: "sign_in_from_signup",
SIGN_IN_WITH_UNIQUE_CODE: "sign_in_with_unique_code",
REQUEST_NEW_CODE: "request_new_code",
VERIFY_CODE: "verify_code",
SET_PASSWORD_FORM: "set_password_form",
};
/**
* ===========================================================================
* Global View Events and Elements
* ===========================================================================
*/
export const GLOBAL_VIEW_TRACKER_EVENTS = {
create: "global_view_created",
update: "global_view_updated",
delete: "global_view_deleted",
open: "global_view_opened",
};
export const GLOBAL_VIEW_TRACKER_ELEMENTS = {
RIGHT_HEADER_ADD_BUTTON: "global_view_right_header_add_button",
HEADER_SAVE_VIEW_BUTTON: "global_view_header_save_view_button",
QUICK_ACTIONS: "global_view_quick_actions",
LIST_ITEM: "global_view_list_item",
};
/**
* ===========================================================================
* Project View Events and Elements
* ===========================================================================
*/
export const PROJECT_VIEW_TRACKER_EVENTS = {
create: "project_view_created",
update: "project_view_updated",
delete: "project_view_deleted",
};
export const PROJECT_VIEW_TRACKER_ELEMENTS = {
RIGHT_HEADER_ADD_BUTTON: "project_view_right_header_add_button",
COMMAND_PALETTE_ADD_ITEM: "command_palette_add_project_view_item",
EMPTY_STATE_CREATE_BUTTON: "project_view_empty_state_create_button",
HEADER_SAVE_VIEW_BUTTON: "project_view_header_save_view_button",
PROJECT_HEADER_SAVE_AS_VIEW_BUTTON: "project_view_header_save_as_view_button",
CYCLE_HEADER_SAVE_AS_VIEW_BUTTON: "cycle_header_save_as_view_button",
MODULE_HEADER_SAVE_AS_VIEW_BUTTON: "module_header_save_as_view_button",
QUICK_ACTIONS: "project_view_quick_actions",
LIST_ITEM_CONTEXT_MENU: "project_view_list_item_context_menu",
};
/**
* ===========================================================================
* Product Tour Events and Elements
* ===========================================================================
*/
export const PRODUCT_TOUR_TRACKER_EVENTS = {
complete: "product_tour_completed",
};
export const PRODUCT_TOUR_TRACKER_ELEMENTS = {
START_BUTTON: "product_tour_start_button",
SKIP_BUTTON: "product_tour_skip_button",
CREATE_PROJECT_BUTTON: "product_tour_create_project_button",
};
/**
* ===========================================================================
* Notification Events and Elements
* ===========================================================================
*/
export const NOTIFICATION_TRACKER_EVENTS = {
archive: "notification_archived",
unarchive: "notification_unarchived",
mark_read: "notification_marked_read",
mark_unread: "notification_marked_unread",
all_marked_read: "all_notifications_marked_read",
};
export const NOTIFICATION_TRACKER_ELEMENTS = {
MARK_ALL_AS_READ_BUTTON: "mark_all_as_read_button",
ARCHIVE_UNARCHIVE_BUTTON: "archive_unarchive_button",
MARK_READ_UNREAD_BUTTON: "mark_read_unread_button",
};
/**
* ===========================================================================
* User Events
* ===========================================================================
*/
export const USER_TRACKER_EVENTS = {
add_details: "user_details_added",
onboarding_complete: "user_onboarding_completed",
};
export const USER_TRACKER_ELEMENTS = {
PRODUCT_CHANGELOG_MODAL: "product_changelog_modal",
CHANGELOG_REDIRECTED: "changelog_redirected",
};
/**
* ===========================================================================
* Onboarding Events and Elements
* ===========================================================================
*/
export const ONBOARDING_TRACKER_ELEMENTS = {
PROFILE_SETUP_FORM: "onboarding_profile_setup_form",
PASSWORD_CREATION_SELECTED: "onboarding_password_creation_selected",
PASSWORD_CREATION_SKIPPED: "onboarding_password_creation_skipped",
};
/**
* ===========================================================================
* Sidebar Events
* ===========================================================================
*/
export const SIDEBAR_TRACKER_ELEMENTS = {
USER_MENU_ITEM: "sidenav_user_menu_item",
CREATE_WORK_ITEM_BUTTON: "sidebar_create_work_item_button",
};
/**
* ===========================================================================
* Project Settings Events and Elements
* ===========================================================================
*/
export const PROJECT_SETTINGS_TRACKER_ELEMENTS = {
LABELS_EMPTY_STATE_CREATE_BUTTON: "labels_empty_state_create_button",
LABELS_HEADER_CREATE_BUTTON: "labels_header_create_button",
LABELS_CONTEXT_MENU: "labels_context_menu",
LABELS_DELETE_BUTTON: "labels_delete_button",
ESTIMATES_TOGGLE_BUTTON: "estimates_toggle_button",
ESTIMATES_EMPTY_STATE_CREATE_BUTTON: "estimates_empty_state_create_button",
ESTIMATES_LIST_ITEM: "estimates_list_item",
AUTOMATIONS_ARCHIVE_TOGGLE_BUTTON: "automations_archive_toggle_button",
AUTOMATIONS_CLOSE_TOGGLE_BUTTON: "automations_close_toggle_button",
};
export const PROJECT_SETTINGS_TRACKER_EVENTS = {
// labels
label_created: "label_created",
label_updated: "label_updated",
label_deleted: "label_deleted",
// estimates
estimate_created: "estimate_created",
estimate_updated: "estimate_updated",
estimate_deleted: "estimate_deleted",
estimates_toggle: "estimates_toggled",
// automations
auto_close_workitems: "auto_close_workitems",
auto_archive_workitems: "auto_archive_workitems",
};
/**
* ===========================================================================
* Profile Settings Events and Elements
* ===========================================================================
*/
export const PROFILE_SETTINGS_TRACKER_EVENTS = {
// Account
deactivate_account: "deactivate_account",
update_profile: "update_profile",
// Preferences
first_day_updated: "first_day_updated",
language_updated: "language_updated",
timezone_updated: "timezone_updated",
theme_updated: "theme_updated",
// Notifications
notifications_updated: "notifications_updated",
// PAT
pat_created: "pat_created",
pat_deleted: "pat_deleted",
};
export const PROFILE_SETTINGS_TRACKER_ELEMENTS = {
// Account
SAVE_CHANGES_BUTTON: "save_changes_button",
DEACTIVATE_ACCOUNT_BUTTON: "deactivate_account_button",
// Preferences
THEME_DROPDOWN: "preferences_theme_dropdown",
FIRST_DAY_OF_WEEK_DROPDOWN: "preferences_first_day_of_week_dropdown",
LANGUAGE_DROPDOWN: "preferences_language_dropdown",
TIMEZONE_DROPDOWN: "preferences_timezone_dropdown",
// Notifications
PROPERTY_CHANGES_TOGGLE: "notifications_property_changes_toggle",
STATE_CHANGES_TOGGLE: "notifications_state_changes_toggle",
COMMENTS_TOGGLE: "notifications_comments_toggle",
MENTIONS_TOGGLE: "notifications_mentions_toggle",
// PAT
HEADER_ADD_PAT_BUTTON: "header_add_pat_button",
EMPTY_STATE_ADD_PAT_BUTTON: "empty_state_add_pat_button",
LIST_ITEM_DELETE_ICON: "list_item_delete_icon",
};
/**
* ===========================================================================
* Workspace Settings Events and Elements
* ===========================================================================
*/
export const WORKSPACE_SETTINGS_TRACKER_EVENTS = {
// Billing
upgrade_plan_redirected: "upgrade_plan_redirected",
// Exports
csv_exported: "csv_exported",
// Webhooks
webhook_created: "webhook_created",
webhook_deleted: "webhook_deleted",
webhook_toggled: "webhook_toggled",
webhook_details_page_toggled: "webhook_details_page_toggled",
webhook_updated: "webhook_updated",
};
export const WORKSPACE_SETTINGS_TRACKER_ELEMENTS = {
// Billing
BILLING_UPGRADE_BUTTON: (subscriptionType: EProductSubscriptionEnum) => `billing_upgrade_${subscriptionType}_button`,
BILLING_TALK_TO_SALES_BUTTON: "billing_talk_to_sales_button",
// Exports
EXPORT_BUTTON: "export_button",
// Webhooks
HEADER_ADD_WEBHOOK_BUTTON: "header_add_webhook_button",
EMPTY_STATE_ADD_WEBHOOK_BUTTON: "empty_state_add_webhook_button",
LIST_ITEM_DELETE_BUTTON: "list_item_delete_button",
WEBHOOK_LIST_ITEM_TOGGLE_SWITCH: "webhook_list_item_toggle_switch",
WEBHOOK_DETAILS_PAGE_TOGGLE_SWITCH: "webhook_details_page_toggle_switch",
WEBHOOK_DELETE_BUTTON: "webhook_delete_button",
WEBHOOK_UPDATE_BUTTON: "webhook_update_button",
};

View File

@@ -0,0 +1 @@
export * from "./core";

View File

@@ -0,0 +1,14 @@
export const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
export const ACCEPTED_AVATAR_IMAGE_MIME_TYPES_FOR_REACT_DROPZONE = {
"image/jpeg": [],
"image/jpg": [],
"image/png": [],
"image/webp": [],
};
export const ACCEPTED_COVER_IMAGE_MIME_TYPES_FOR_REACT_DROPZONE = {
"image/jpeg": [],
"image/jpg": [],
"image/png": [],
"image/webp": [],
};

View File

@@ -0,0 +1,57 @@
export enum E_SORT_ORDER {
ASC = "asc",
DESC = "desc",
}
export const DATE_AFTER_FILTER_OPTIONS = [
{
name: "1 week from now",
value: "1_weeks;after;fromnow",
},
{
name: "2 weeks from now",
value: "2_weeks;after;fromnow",
},
{
name: "1 month from now",
value: "1_months;after;fromnow",
},
{
name: "2 months from now",
value: "2_months;after;fromnow",
},
];
export const DATE_BEFORE_FILTER_OPTIONS = [
{
name: "1 week ago",
value: "1_weeks;before;fromnow",
},
{
name: "2 weeks ago",
value: "2_weeks;before;fromnow",
},
{
name: "1 month ago",
i18n_name: "date_filters.1_month_ago",
value: "1_months;before;fromnow",
},
];
export const PROJECT_CREATED_AT_FILTER_OPTIONS = [
{
name: "Today",
value: "today;custom;custom",
},
{
name: "Yesterday",
value: "yesterday;custom;custom",
},
{
name: "Last 7 days",
value: "last_7_days;custom;custom",
},
{
name: "Last 30 days",
value: "last_30_days;custom;custom",
},
];

View File

@@ -0,0 +1,34 @@
export const CHARTS_THEME = {
background: "transparent",
text: {
color: "rgb(var(--color-text-200))",
},
axis: {
domain: {
line: {
stroke: "rgb(var(--color-background-80))",
strokeWidth: 0.5,
},
},
},
tooltip: {
container: {
background: "rgb(var(--color-background-80))",
color: "rgb(var(--color-text-200))",
fontSize: "0.8rem",
border: "1px solid rgb(var(--color-border-300))",
},
},
grid: {
line: {
stroke: "rgb(var(--color-border-100))",
},
},
};
export const CHART_DEFAULT_MARGIN = {
top: 50,
right: 50,
bottom: 50,
left: 50,
};

View File

@@ -0,0 +1,7 @@
export enum EIconSize {
XS = "xs",
SM = "sm",
MD = "md",
LG = "lg",
XL = "xl",
}

View File

@@ -0,0 +1,40 @@
export * from "./ai";
export * from "./analytics";
export * from "./auth";
export * from "./chart";
export * from "./cycle";
export * from "./dashboard";
export * from "./emoji";
export * from "./endpoints";
export * from "./estimates";
export * from "./event-tracker";
export * from "./file";
export * from "./filter";
export * from "./graph";
export * from "./icon";
export * from "./instance";
export * from "./intake";
export * from "./issue";
export * from "./members";
export * from "./label";
export * from "./metadata";
export * from "./module";
export * from "./notification";
export * from "./page";
export * from "./payment";
export * from "./profile";
export * from "./project";
export * from "./rich-filters";
export * from "./settings";
export * from "./sidebar";
export * from "./spreadsheet";
export * from "./state";
export * from "./stickies";
export * from "./subscription";
export * from "./swr";
export * from "./tab-indices";
export * from "./themes";
export * from "./user";
export * from "./views";
export * from "./workspace-drafts";
export * from "./workspace";

View File

@@ -0,0 +1,9 @@
export enum EInstanceStatus {
ERROR = "ERROR",
NOT_YET_READY = "NOT_YET_READY",
}
export type TInstanceStatus = {
status: EInstanceStatus | undefined;
data?: object;
};

View File

@@ -0,0 +1,94 @@
import { EInboxIssueStatus, TInboxIssueStatus } from "@plane/types";
export const INBOX_STATUS: {
key: string;
status: TInboxIssueStatus;
i18n_title: string;
i18n_description: () => string;
}[] = [
{
key: "pending",
i18n_title: "inbox_issue.status.pending.title",
status: EInboxIssueStatus.PENDING,
i18n_description: () => `inbox_issue.status.pending.description`,
},
{
key: "declined",
i18n_title: "inbox_issue.status.declined.title",
status: EInboxIssueStatus.DECLINED,
i18n_description: () => `inbox_issue.status.declined.description`,
},
{
key: "snoozed",
i18n_title: "inbox_issue.status.snoozed.title",
status: EInboxIssueStatus.SNOOZED,
i18n_description: () => `inbox_issue.status.snoozed.description`,
},
{
key: "accepted",
i18n_title: "inbox_issue.status.accepted.title",
status: EInboxIssueStatus.ACCEPTED,
i18n_description: () => `inbox_issue.status.accepted.description`,
},
{
key: "duplicate",
i18n_title: "inbox_issue.status.duplicate.title",
status: EInboxIssueStatus.DUPLICATE,
i18n_description: () => `inbox_issue.status.duplicate.description`,
},
];
export const INBOX_ISSUE_ORDER_BY_OPTIONS = [
{
key: "issue__created_at",
i18n_label: "inbox_issue.order_by.created_at",
},
{
key: "issue__updated_at",
i18n_label: "inbox_issue.order_by.updated_at",
},
{
key: "issue__sequence_id",
i18n_label: "inbox_issue.order_by.id",
},
];
export const INBOX_ISSUE_SORT_BY_OPTIONS = [
{
key: "asc",
i18n_label: "common.sort.asc",
},
{
key: "desc",
i18n_label: "common.sort.desc",
},
];
export enum EPastDurationFilters {
TODAY = "today",
YESTERDAY = "yesterday",
LAST_7_DAYS = "last_7_days",
LAST_30_DAYS = "last_30_days",
}
export const PAST_DURATION_FILTER_OPTIONS: {
name: string;
value: string;
}[] = [
{
name: "Today",
value: EPastDurationFilters.TODAY,
},
{
name: "Yesterday",
value: EPastDurationFilters.YESTERDAY,
},
{
name: "Last 7 days",
value: EPastDurationFilters.LAST_7_DAYS,
},
{
name: "Last 30 days",
value: EPastDurationFilters.LAST_30_DAYS,
},
];

View File

@@ -0,0 +1,364 @@
import {
TIssueGroupByOptions,
TIssueOrderByOptions,
IIssueDisplayProperties,
IIssueFilterOptions,
TIssue,
EIssuesStoreType,
} from "@plane/types";
export const ALL_ISSUES = "All Issues";
export type TIssuePriorities = "urgent" | "high" | "medium" | "low" | "none";
export type TIssueFilterPriorityObject = {
key: TIssuePriorities;
titleTranslationKey: string;
className: string;
icon: string;
};
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",
// eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
"team_project" = "project_id",
}
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 enum EIssueCommentAccessSpecifier {
EXTERNAL = "EXTERNAL",
INTERNAL = "INTERNAL",
}
export enum EIssueListRow {
HEADER = "HEADER",
ISSUE = "ISSUE",
NO_ISSUES = "NO_ISSUES",
QUICK_ADD = "QUICK_ADD",
}
export const ISSUE_PRIORITIES: {
key: TIssuePriorities;
title: string;
}[] = [
{
key: "urgent",
title: "Urgent",
},
{
key: "high",
title: "High",
},
{
key: "medium",
title: "Medium",
},
{
key: "low",
title: "Low",
},
{
key: "none",
title: "None",
},
];
export const DRAG_ALLOWED_GROUPS: TIssueGroupByOptions[] = [
"state",
"priority",
"assignees",
"labels",
"module",
"cycle",
];
export type TCreateModalStoreTypes =
| EIssuesStoreType.TEAM
| EIssuesStoreType.PROJECT
| EIssuesStoreType.TEAM_VIEW
| EIssuesStoreType.PROJECT_VIEW
| EIssuesStoreType.PROFILE
| EIssuesStoreType.CYCLE
| EIssuesStoreType.MODULE
| EIssuesStoreType.EPIC
| EIssuesStoreType.TEAM_PROJECT_WORK_ITEMS;
export const ISSUE_GROUP_BY_OPTIONS: {
key: TIssueGroupByOptions;
titleTranslationKey: string;
}[] = [
{ key: "state", titleTranslationKey: "common.states" },
{ key: "state_detail.group", titleTranslationKey: "common.state_groups" },
{ key: "priority", titleTranslationKey: "common.priority" },
{ key: "team_project", titleTranslationKey: "common.team_project" }, // required this on team issues
{ key: "project", titleTranslationKey: "common.project" }, // required this on my issues
{ key: "cycle", titleTranslationKey: "common.cycle" }, // required this on my issues
{ key: "module", titleTranslationKey: "common.module" }, // required this on my issues
{ key: "labels", titleTranslationKey: "common.labels" },
{ key: "assignees", titleTranslationKey: "common.assignees" },
{ key: "created_by", titleTranslationKey: "common.created_by" },
{ key: null, titleTranslationKey: "common.none" },
];
export const ISSUE_ORDER_BY_OPTIONS: {
key: TIssueOrderByOptions;
titleTranslationKey: string;
}[] = [
{ key: "sort_order", titleTranslationKey: "common.order_by.manual" },
{ key: "-created_at", titleTranslationKey: "common.order_by.last_created" },
{ key: "-updated_at", titleTranslationKey: "common.order_by.last_updated" },
{ key: "start_date", titleTranslationKey: "common.order_by.start_date" },
{ key: "target_date", titleTranslationKey: "common.order_by.due_date" },
{ key: "-priority", titleTranslationKey: "common.priority" },
];
export const ISSUE_DISPLAY_PROPERTIES_KEYS: (keyof IIssueDisplayProperties)[] = [
"assignee",
"start_date",
"due_date",
"labels",
"key",
"priority",
"state",
"sub_issue_count",
"link",
"attachment_count",
"estimate",
"created_on",
"updated_on",
"modules",
"cycle",
"issue_type",
];
export const SUB_ISSUES_DISPLAY_PROPERTIES_KEYS: (keyof IIssueDisplayProperties)[] = [
"key",
"assignee",
"start_date",
"due_date",
"priority",
"state",
];
export const ISSUE_DISPLAY_PROPERTIES: {
key: keyof IIssueDisplayProperties;
titleTranslationKey: string;
}[] = [
{
key: "key",
titleTranslationKey: "issue.display.properties.id",
},
{
key: "issue_type",
titleTranslationKey: "issue.display.properties.issue_type",
},
{
key: "assignee",
titleTranslationKey: "common.assignee",
},
{
key: "start_date",
titleTranslationKey: "common.order_by.start_date",
},
{
key: "due_date",
titleTranslationKey: "common.order_by.due_date",
},
{ key: "labels", titleTranslationKey: "common.labels" },
{
key: "priority",
titleTranslationKey: "common.priority",
},
{ key: "state", titleTranslationKey: "common.state" },
{
key: "sub_issue_count",
titleTranslationKey: "issue.display.properties.sub_issue_count",
},
{
key: "attachment_count",
titleTranslationKey: "issue.display.properties.attachment_count",
},
{ key: "link", titleTranslationKey: "common.link" },
{
key: "estimate",
titleTranslationKey: "common.estimate",
},
{ key: "modules", titleTranslationKey: "common.module" },
{ key: "cycle", titleTranslationKey: "common.cycle" },
];
export const SPREADSHEET_PROPERTY_LIST: (keyof IIssueDisplayProperties)[] = [
"state",
"priority",
"assignee",
"labels",
"modules",
"cycle",
"start_date",
"due_date",
"estimate",
"created_on",
"updated_on",
"link",
"attachment_count",
"sub_issue_count",
];
export const SPREADSHEET_PROPERTY_DETAILS: {
[key in keyof IIssueDisplayProperties]: {
i18n_title: string;
ascendingOrderKey: TIssueOrderByOptions;
ascendingOrderTitle: string;
descendingOrderKey: TIssueOrderByOptions;
descendingOrderTitle: string;
icon: string;
};
} = {
assignee: {
i18n_title: "common.assignees",
ascendingOrderKey: "assignees__first_name",
ascendingOrderTitle: "A",
descendingOrderKey: "-assignees__first_name",
descendingOrderTitle: "Z",
icon: "MembersPropertyIcon",
},
created_on: {
i18n_title: "common.sort.created_on",
ascendingOrderKey: "-created_at",
ascendingOrderTitle: "New",
descendingOrderKey: "created_at",
descendingOrderTitle: "Old",
icon: "CalendarDays",
},
due_date: {
i18n_title: "common.order_by.due_date",
ascendingOrderKey: "-target_date",
ascendingOrderTitle: "New",
descendingOrderKey: "target_date",
descendingOrderTitle: "Old",
icon: "DueDatePropertyIcon",
},
estimate: {
i18n_title: "common.estimate",
ascendingOrderKey: "estimate_point__key",
ascendingOrderTitle: "Low",
descendingOrderKey: "-estimate_point__key",
descendingOrderTitle: "High",
icon: "EstimatePropertyIcon",
},
labels: {
i18n_title: "common.labels",
ascendingOrderKey: "labels__name",
ascendingOrderTitle: "A",
descendingOrderKey: "-labels__name",
descendingOrderTitle: "Z",
icon: "LabelPropertyIcon",
},
modules: {
i18n_title: "common.modules",
ascendingOrderKey: "issue_module__module__name",
ascendingOrderTitle: "A",
descendingOrderKey: "-issue_module__module__name",
descendingOrderTitle: "Z",
icon: "DiceIcon",
},
cycle: {
i18n_title: "common.cycle",
ascendingOrderKey: "issue_cycle__cycle__name",
ascendingOrderTitle: "A",
descendingOrderKey: "-issue_cycle__cycle__name",
descendingOrderTitle: "Z",
icon: "ContrastIcon",
},
priority: {
i18n_title: "common.priority",
ascendingOrderKey: "priority",
ascendingOrderTitle: "None",
descendingOrderKey: "-priority",
descendingOrderTitle: "Urgent",
icon: "PriorityPropertyIcon",
},
start_date: {
i18n_title: "common.order_by.start_date",
ascendingOrderKey: "-start_date",
ascendingOrderTitle: "New",
descendingOrderKey: "start_date",
descendingOrderTitle: "Old",
icon: "StartDatePropertyIcon",
},
state: {
i18n_title: "common.state",
ascendingOrderKey: "state__name",
ascendingOrderTitle: "A",
descendingOrderKey: "-state__name",
descendingOrderTitle: "Z",
icon: "StatePropertyIcon",
},
updated_on: {
i18n_title: "common.sort.updated_on",
ascendingOrderKey: "-updated_at",
ascendingOrderTitle: "New",
descendingOrderKey: "updated_at",
descendingOrderTitle: "Old",
icon: "CalendarDays",
},
link: {
i18n_title: "common.link",
ascendingOrderKey: "-link_count",
ascendingOrderTitle: "Most",
descendingOrderKey: "link_count",
descendingOrderTitle: "Least",
icon: "Link2",
},
attachment_count: {
i18n_title: "common.attachment",
ascendingOrderKey: "-attachment_count",
ascendingOrderTitle: "Most",
descendingOrderKey: "attachment_count",
descendingOrderTitle: "Least",
icon: "Paperclip",
},
sub_issue_count: {
i18n_title: "issue.display.properties.sub_issue",
ascendingOrderKey: "-sub_issues_count",
ascendingOrderTitle: "Most",
descendingOrderKey: "sub_issues_count",
descendingOrderTitle: "Least",
icon: "LayersIcon",
},
};
// Map filter keys to their corresponding issue property keys
export const FILTER_TO_ISSUE_MAP: Partial<Record<keyof IIssueFilterOptions, keyof TIssue>> = {
assignees: "assignee_ids",
created_by: "created_by",
labels: "label_ids",
priority: "priority",
cycle: "cycle_id",
module: "module_ids",
project: "project_id",
state: "state_id",
issue_type: "type_id",
state_group: "state__group",
} as const;

View File

@@ -0,0 +1,358 @@
import {
EIssuesStoreType,
IIssueFilterOptions,
ILayoutDisplayFiltersOptions,
TIssueActivityComment,
TWorkItemFilterProperty,
} from "@plane/types";
import {
TIssueFilterPriorityObject,
ISSUE_DISPLAY_PROPERTIES_KEYS,
SUB_ISSUES_DISPLAY_PROPERTIES_KEYS,
} from "./common";
import { TIssueLayout } from "./layout";
export type TIssueFilterKeys = "priority" | "state" | "labels";
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 enum EIssueFilterType {
FILTERS = "rich_filters",
DISPLAY_FILTERS = "display_filters",
DISPLAY_PROPERTIES = "display_properties",
KANBAN_FILTERS = "kanban_filters",
}
export type TSupportedFilterTypeForUpdate =
| EIssueFilterType.DISPLAY_FILTERS
| EIssueFilterType.DISPLAY_PROPERTIES
| EIssueFilterType.KANBAN_FILTERS;
export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: {
[key in TIssueLayout]: Record<"filters", TIssueFilterKeys[]>;
} = {
list: {
filters: ["priority", "state", "labels"],
},
kanban: {
filters: ["priority", "state", "labels"],
},
calendar: {
filters: ["priority", "state", "labels"],
},
spreadsheet: {
filters: ["priority", "state", "labels"],
},
gantt: {
filters: ["priority", "state", "labels"],
},
};
export const ISSUE_PRIORITY_FILTERS: TIssueFilterPriorityObject[] = [
{
key: "urgent",
titleTranslationKey: "issue.priority.urgent",
className: "bg-red-500 border-red-500 text-white",
icon: "error",
},
{
key: "high",
titleTranslationKey: "issue.priority.high",
className: "text-orange-500 border-custom-border-300",
icon: "signal_cellular_alt",
},
{
key: "medium",
titleTranslationKey: "issue.priority.medium",
className: "text-yellow-500 border-custom-border-300",
icon: "signal_cellular_alt_2_bar",
},
{
key: "low",
titleTranslationKey: "issue.priority.low",
className: "text-green-500 border-custom-border-300",
icon: "signal_cellular_alt_1_bar",
},
{
key: "none",
titleTranslationKey: "common.none",
className: "text-gray-500 border-custom-border-300",
icon: "block",
},
];
export type TFiltersLayoutOptions = {
[layoutType: string]: ILayoutDisplayFiltersOptions;
};
export type TFilterPropertiesByPageType = {
filters: TWorkItemFilterProperty[];
layoutOptions: TFiltersLayoutOptions;
};
export type TIssueFiltersToDisplayByPageType = {
[pageType: string]: TFilterPropertiesByPageType;
};
export const ISSUE_DISPLAY_FILTERS_BY_PAGE: TIssueFiltersToDisplayByPageType = {
profile_issues: {
filters: ["priority", "state_group", "label_id", "start_date", "target_date"],
layoutOptions: {
list: {
display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS,
display_filters: {
group_by: ["state_detail.group", "priority", "project", "labels", null],
order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"],
type: ["active", "backlog"],
},
extra_options: {
access: true,
values: ["show_empty_groups", "sub_issue"],
},
},
kanban: {
display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS,
display_filters: {
group_by: ["state_detail.group", "priority", "project", "labels"],
order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"],
type: ["active", "backlog"],
},
extra_options: {
access: true,
values: ["show_empty_groups"],
},
},
},
},
archived_issues: {
filters: [
"priority",
"state_group",
"state_id",
"cycle_id",
"module_id",
"assignee_id",
"created_by_id",
"label_id",
"start_date",
"target_date",
],
layoutOptions: {
list: {
display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS,
display_filters: {
group_by: ["state", "cycle", "module", "priority", "labels", "assignees", "created_by", null],
order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"],
type: ["active", "backlog"],
},
extra_options: {
access: true,
values: ["show_empty_groups"],
},
},
},
},
my_issues: {
filters: [
"priority",
"state_group",
"label_id",
"assignee_id",
"created_by_id",
"subscriber_id",
"project_id",
"start_date",
"target_date",
],
layoutOptions: {
spreadsheet: {
display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS,
display_filters: {
order_by: [],
type: ["active", "backlog"],
},
extra_options: {
access: true,
values: ["sub_issue"],
},
},
list: {
display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS,
display_filters: {
type: ["active", "backlog"],
},
extra_options: {
access: false,
values: [],
},
},
},
},
issues: {
filters: [
"priority",
"state_group",
"state_id",
"cycle_id",
"module_id",
"assignee_id",
"mention_id",
"created_by_id",
"label_id",
"start_date",
"target_date",
],
layoutOptions: {
list: {
display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS,
display_filters: {
group_by: ["state", "priority", "cycle", "module", "labels", "assignees", "created_by", null],
order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority", "target_date"],
type: ["active", "backlog"],
},
extra_options: {
access: true,
values: ["show_empty_groups", "sub_issue"],
},
},
kanban: {
display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS,
display_filters: {
group_by: ["state", "priority", "cycle", "module", "labels", "assignees", "created_by"],
sub_group_by: ["state", "priority", "cycle", "module", "labels", "assignees", "created_by", null],
order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority", "target_date"],
type: ["active", "backlog"],
},
extra_options: {
access: true,
values: ["show_empty_groups", "sub_issue"],
},
},
calendar: {
display_properties: ["key", "issue_type"],
display_filters: {
type: ["active", "backlog"],
},
extra_options: {
access: true,
values: ["sub_issue"],
},
},
spreadsheet: {
display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS,
display_filters: {
order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"],
type: ["active", "backlog"],
},
extra_options: {
access: true,
values: ["sub_issue"],
},
},
gantt_chart: {
display_properties: ["key", "issue_type"],
display_filters: {
order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"],
type: ["active", "backlog"],
},
extra_options: {
access: true,
values: ["sub_issue"],
},
},
},
},
sub_work_items: {
filters: ["priority", "state_id", "assignee_id", "start_date", "target_date"],
layoutOptions: {
list: {
display_properties: SUB_ISSUES_DISPLAY_PROPERTIES_KEYS,
display_filters: {
order_by: ["-created_at", "-updated_at", "start_date", "-priority"],
group_by: ["state", "priority", "assignees", null],
},
extra_options: {
access: true,
values: ["sub_issue"],
},
},
},
},
};
export const ISSUE_STORE_TO_FILTERS_MAP: Partial<Record<EIssuesStoreType, TFilterPropertiesByPageType>> = {
[EIssuesStoreType.PROJECT]: ISSUE_DISPLAY_FILTERS_BY_PAGE.issues,
};
export const SUB_WORK_ITEM_AVAILABLE_FILTERS_FOR_WORK_ITEM_PAGE: (keyof IIssueFilterOptions)[] = [
"priority",
"state",
"issue_type",
"assignees",
"start_date",
"target_date",
];
export enum EActivityFilterType {
ACTIVITY = "ACTIVITY",
COMMENT = "COMMENT",
STATE = "STATE",
ASSIGNEE = "ASSIGNEE",
DEFAULT = "DEFAULT",
}
export type TActivityFilters = EActivityFilterType;
export type TActivityFilterOptionsKey = Exclude<TActivityFilters, EActivityFilterType.DEFAULT>;
export const ACTIVITY_FILTER_TYPE_OPTIONS: Record<TActivityFilterOptionsKey, { labelTranslationKey: string }> = {
[EActivityFilterType.ACTIVITY]: {
labelTranslationKey: "common.updates",
},
[EActivityFilterType.COMMENT]: {
labelTranslationKey: "common.comments",
},
[EActivityFilterType.STATE]: {
labelTranslationKey: "common.state",
},
[EActivityFilterType.ASSIGNEE]: {
labelTranslationKey: "common.assignee",
},
};
export type TActivityFilterOption = {
key: TActivityFilters;
labelTranslationKey: string;
isSelected: boolean;
onClick: () => void;
};
export const defaultActivityFilters: TActivityFilters[] = [
EActivityFilterType.ACTIVITY,
EActivityFilterType.COMMENT,
EActivityFilterType.STATE,
EActivityFilterType.ASSIGNEE,
];
export const filterActivityOnSelectedFilters = (
activity: TIssueActivityComment[],
filters: TActivityFilters[]
): TIssueActivityComment[] =>
activity.filter((activity) => {
if (activity.activity_type === EActivityFilterType.DEFAULT) return true;
return filters.includes(activity.activity_type as TActivityFilters);
});
export const ENABLE_ISSUE_DEPENDENCIES = false;

View File

@@ -0,0 +1,4 @@
export * from "./common";
export * from "./filter";
export * from "./layout";
export * from "./modal";

View File

@@ -0,0 +1,63 @@
import { EIssueLayoutTypes } from "@plane/types";
export type TIssueLayout = "list" | "kanban" | "calendar" | "spreadsheet" | "gantt";
export type TIssueLayoutMap = Record<
EIssueLayoutTypes,
{
key: EIssueLayoutTypes;
i18n_title: string;
i18n_label: string;
}
>;
export const SITES_ISSUE_LAYOUTS: {
key: TIssueLayout;
titleTranslationKey: string;
icon: string;
}[] = [
{
key: "list",
icon: "List",
titleTranslationKey: "issue.layouts.list",
},
{
key: "kanban",
icon: "Kanban",
titleTranslationKey: "issue.layouts.kanban",
},
];
export const ISSUE_LAYOUT_MAP: TIssueLayoutMap = {
[EIssueLayoutTypes.LIST]: {
key: EIssueLayoutTypes.LIST,
i18n_title: "issue.layouts.title.list",
i18n_label: "issue.layouts.list",
},
[EIssueLayoutTypes.KANBAN]: {
key: EIssueLayoutTypes.KANBAN,
i18n_title: "issue.layouts.title.kanban",
i18n_label: "issue.layouts.kanban",
},
[EIssueLayoutTypes.CALENDAR]: {
key: EIssueLayoutTypes.CALENDAR,
i18n_title: "issue.layouts.title.calendar",
i18n_label: "issue.layouts.calendar",
},
[EIssueLayoutTypes.SPREADSHEET]: {
key: EIssueLayoutTypes.SPREADSHEET,
i18n_title: "issue.layouts.title.spreadsheet",
i18n_label: "issue.layouts.spreadsheet",
},
[EIssueLayoutTypes.GANTT]: {
key: EIssueLayoutTypes.GANTT,
i18n_title: "issue.layouts.title.gantt",
i18n_label: "issue.layouts.gantt",
},
};
export const ISSUE_LAYOUTS: {
key: EIssueLayoutTypes;
i18n_title: string;
i18n_label: string;
}[] = Object.values(ISSUE_LAYOUT_MAP);

View File

@@ -0,0 +1,19 @@
// plane imports
import { TIssue } from "@plane/types";
export const DEFAULT_WORK_ITEM_FORM_VALUES: Partial<TIssue> = {
project_id: "",
type_id: null,
name: "",
description_html: "",
estimate_point: null,
state_id: "",
parent_id: null,
priority: "none",
assignee_ids: [],
label_ids: [],
cycle_id: null,
module_ids: null,
start_date: null,
target_date: null,
};

View File

@@ -0,0 +1,17 @@
export const LABEL_COLOR_OPTIONS = [
"#FF6900",
"#FCB900",
"#7BDCB5",
"#00D084",
"#8ED1FC",
"#0693E3",
"#ABB8C3",
"#EB144C",
"#F78DA7",
"#9900EF",
];
export const getRandomLabelColor = () => {
const randomIndex = Math.floor(Math.random() * LABEL_COLOR_OPTIONS.length);
return LABEL_COLOR_OPTIONS[randomIndex];
};

View File

@@ -0,0 +1,79 @@
// Member property constants - Single source of truth for member spreadsheet properties
export type TMemberOrderByOptions =
| "display_name"
| "-display_name"
| "full_name"
| "-full_name"
| "email"
| "-email"
| "joining_date"
| "-joining_date"
| "role"
| "-role";
export interface IProjectMemberDisplayProperties {
full_name: boolean;
display_name: boolean;
email: boolean;
joining_date: boolean;
role: boolean;
}
export const MEMBER_PROPERTY_DETAILS: {
[key in keyof IProjectMemberDisplayProperties]: {
i18n_title: string;
ascendingOrderKey: TMemberOrderByOptions;
ascendingOrderTitle: string;
descendingOrderKey: TMemberOrderByOptions;
descendingOrderTitle: string;
iconName: string;
isSortingAllowed: boolean;
};
} = {
full_name: {
i18n_title: "project_members.full_name",
ascendingOrderKey: "full_name",
ascendingOrderTitle: "A",
descendingOrderKey: "-full_name",
descendingOrderTitle: "Z",
iconName: "User",
isSortingAllowed: true,
},
display_name: {
i18n_title: "project_members.display_name",
ascendingOrderKey: "display_name",
ascendingOrderTitle: "A",
descendingOrderKey: "-display_name",
descendingOrderTitle: "Z",
iconName: "User",
isSortingAllowed: true,
},
email: {
i18n_title: "project_members.email",
ascendingOrderKey: "email",
ascendingOrderTitle: "A",
descendingOrderKey: "-email",
descendingOrderTitle: "Z",
iconName: "Mail",
isSortingAllowed: true,
},
joining_date: {
i18n_title: "project_members.joining_date",
ascendingOrderKey: "joining_date",
ascendingOrderTitle: "Old",
descendingOrderKey: "-joining_date",
descendingOrderTitle: "New",
iconName: "Calendar",
isSortingAllowed: true,
},
role: {
i18n_title: "project_members.role",
ascendingOrderKey: "role",
ascendingOrderTitle: "Guest",
descendingOrderKey: "-role",
descendingOrderTitle: "Admin",
iconName: "Shield",
isSortingAllowed: true,
},
};

View File

@@ -0,0 +1,17 @@
export const SITE_NAME = "Plane | Simple, extensible, open-source project management tool.";
export const SITE_TITLE = "Plane | Simple, extensible, open-source project management tool.";
export const SITE_DESCRIPTION =
"Open-source project management tool to manage work items, cycles, and product roadmaps easily";
export const SITE_KEYWORDS =
"software development, plan, ship, software, accelerate, code management, release management, project management, work items tracking, agile, scrum, kanban, collaboration";
export const SITE_URL = "https://app.plane.so/";
export const TWITTER_USER_NAME = "Plane | Simple, extensible, open-source project management tool.";
// Plane Sites Metadata
export const SPACE_SITE_NAME = "Plane Publish | Make your Plane boards and roadmaps pubic with just one-click. ";
export const SPACE_SITE_TITLE = "Plane Publish | Make your Plane boards public with one-click";
export const SPACE_SITE_DESCRIPTION = "Plane Publish is a customer feedback management tool built on top of plane.so";
export const SPACE_SITE_KEYWORDS =
"software development, customer feedback, software, accelerate, code management, release management, project management, work items tracking, agile, scrum, kanban, collaboration";
export const SPACE_SITE_URL = "https://app.plane.so/";
export const SPACE_TWITTER_USER_NAME = "planepowers";

View File

@@ -0,0 +1,112 @@
// 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",
};
export const MODULE_STATUS: {
i18n_label: string;
value: TModuleStatus;
color: string;
textColor: string;
bgColor: string;
}[] = [
{
i18n_label: "project_modules.status.backlog",
value: "backlog",
color: MODULE_STATUS_COLORS.backlog,
textColor: "text-custom-text-400",
bgColor: "bg-custom-background-80",
},
{
i18n_label: "project_modules.status.planned",
value: "planned",
color: MODULE_STATUS_COLORS.planned,
textColor: "text-blue-500",
bgColor: "bg-indigo-50",
},
{
i18n_label: "project_modules.status.in_progress",
value: "in-progress",
color: MODULE_STATUS_COLORS["in-progress"],
textColor: "text-amber-500",
bgColor: "bg-amber-50",
},
{
i18n_label: "project_modules.status.paused",
value: "paused",
color: MODULE_STATUS_COLORS.paused,
textColor: "text-custom-text-300",
bgColor: "bg-custom-background-90",
},
{
i18n_label: "project_modules.status.completed",
value: "completed",
color: MODULE_STATUS_COLORS.completed,
textColor: "text-green-600",
bgColor: "bg-green-100",
},
{
i18n_label: "project_modules.status.cancelled",
value: "cancelled",
color: MODULE_STATUS_COLORS.cancelled,
textColor: "text-red-500",
bgColor: "bg-red-50",
},
];
export const MODULE_VIEW_LAYOUTS: {
key: TModuleLayoutOptions;
i18n_title: string;
}[] = [
{
key: "list",
i18n_title: "project_modules.layout.list",
},
{
key: "board",
i18n_title: "project_modules.layout.board",
},
{
key: "gantt",
i18n_title: "project_modules.layout.timeline",
},
];
export const MODULE_ORDER_BY_OPTIONS: {
key: TModuleOrderByOptions;
i18n_label: string;
}[] = [
{
key: "name",
i18n_label: "project_modules.order_by.name",
},
{
key: "progress",
i18n_label: "project_modules.order_by.progress",
},
{
key: "issues_length",
i18n_label: "project_modules.order_by.issues",
},
{
key: "target_date",
i18n_label: "project_modules.order_by.due_date",
},
{
key: "created_at",
i18n_label: "project_modules.order_by.created_at",
},
{
key: "sort_order",
i18n_label: "project_modules.order_by.manual",
},
];

View File

@@ -0,0 +1,136 @@
import { TUnreadNotificationsCount } from "@plane/types";
export enum ENotificationTab {
ALL = "all",
MENTIONS = "mentions",
}
export enum ENotificationFilterType {
CREATED = "created",
ASSIGNED = "assigned",
SUBSCRIBED = "subscribed",
}
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 enum ENotificationQueryParamType {
INIT = "init",
CURRENT = "current",
NEXT = "next",
}
export type TNotificationTab = ENotificationTab.ALL | ENotificationTab.MENTIONS;
export const NOTIFICATION_TABS = [
{
i18n_label: "notification.tabs.all",
value: ENotificationTab.ALL,
count: (unReadNotification: TUnreadNotificationsCount) => unReadNotification?.total_unread_notifications_count || 0,
},
{
i18n_label: "notification.tabs.mentions",
value: ENotificationTab.MENTIONS,
count: (unReadNotification: TUnreadNotificationsCount) =>
unReadNotification?.mention_unread_notifications_count || 0,
},
];
export const FILTER_TYPE_OPTIONS = [
{
i18n_label: "notification.filter.assigned",
value: ENotificationFilterType.ASSIGNED,
},
{
i18n_label: "notification.filter.created",
value: ENotificationFilterType.CREATED,
},
{
i18n_label: "notification.filter.subscribed",
value: ENotificationFilterType.SUBSCRIBED,
},
];
export const NOTIFICATION_SNOOZE_OPTIONS = [
{
key: "1_day",
i18n_label: "notification.snooze.1_day",
value: () => {
const date = new Date();
return new Date(date.getTime() + 24 * 60 * 60 * 1000);
},
},
{
key: "3_days",
i18n_label: "notification.snooze.3_days",
value: () => {
const date = new Date();
return new Date(date.getTime() + 3 * 24 * 60 * 60 * 1000);
},
},
{
key: "5_days",
i18n_label: "notification.snooze.5_days",
value: () => {
const date = new Date();
return new Date(date.getTime() + 5 * 24 * 60 * 60 * 1000);
},
},
{
key: "1_week",
i18n_label: "notification.snooze.1_week",
value: () => {
const date = new Date();
return new Date(date.getTime() + 7 * 24 * 60 * 60 * 1000);
},
},
{
key: "2_weeks",
i18n_label: "notification.snooze.2_weeks",
value: () => {
const date = new Date();
return new Date(date.getTime() + 14 * 24 * 60 * 60 * 1000);
},
},
{
key: "custom",
i18n_label: "notification.snooze.custom",
value: undefined,
},
];
// Constant for all time values in 30 minutes interval in 12 hours format
export const allTimeIn30MinutesInterval12HoursFormat: Array<{
label: string;
value: string;
}> = [
{ label: "12:00", value: "12:00" },
{ label: "12:30", value: "12:30" },
{ label: "01:00", value: "01:00" },
{ label: "01:30", value: "01:30" },
{ label: "02:00", value: "02:00" },
{ label: "02:30", value: "02:30" },
{ label: "03:00", value: "03:00" },
{ label: "03:30", value: "03:30" },
{ label: "04:00", value: "04:00" },
{ label: "04:30", value: "04:30" },
{ label: "05:00", value: "05:00" },
{ label: "05:30", value: "05:30" },
{ label: "06:00", value: "06:00" },
{ label: "06:30", value: "06:30" },
{ label: "07:00", value: "07:00" },
{ label: "07:30", value: "07:30" },
{ label: "08:00", value: "08:00" },
{ label: "08:30", value: "08:30" },
{ label: "09:00", value: "09:00" },
{ label: "09:30", value: "09:30" },
{ label: "10:00", value: "10:00" },
{ label: "10:30", value: "10:30" },
{ label: "11:00", value: "11:00" },
{ label: "11:30", value: "11:30" },
];

View File

@@ -0,0 +1,14 @@
export enum EPageAccess {
PUBLIC = 0,
PRIVATE = 1,
}
export type TCreatePageModal = {
isOpen: boolean;
pageAccess?: EPageAccess;
};
export const DEFAULT_CREATE_PAGE_MODAL_DATA: TCreatePageModal = {
isOpen: false,
pageAccess: EPageAccess.PUBLIC,
};

View File

@@ -0,0 +1,152 @@
import { EProductSubscriptionEnum, IPaymentProduct, TBillingFrequency, TProductBillingFrequency } from "@plane/types";
/**
* Default billing frequency for each product subscription type
*/
export const DEFAULT_PRODUCT_BILLING_FREQUENCY: TProductBillingFrequency = {
[EProductSubscriptionEnum.FREE]: undefined,
[EProductSubscriptionEnum.ONE]: undefined,
[EProductSubscriptionEnum.PRO]: "month",
[EProductSubscriptionEnum.BUSINESS]: "month",
[EProductSubscriptionEnum.ENTERPRISE]: "month",
};
/**
* Subscription types that support billing frequency toggle (monthly/yearly)
*/
export const SUBSCRIPTION_WITH_BILLING_FREQUENCY = [
EProductSubscriptionEnum.PRO,
EProductSubscriptionEnum.BUSINESS,
EProductSubscriptionEnum.ENTERPRISE,
];
/**
* Mapping of product subscription types to their respective payment product details
* Used to provide information about each product's pricing and features
*/
export const PLANE_COMMUNITY_PRODUCTS: Record<string, IPaymentProduct> = {
[EProductSubscriptionEnum.PRO]: {
id: EProductSubscriptionEnum.PRO,
name: "Plane Pro",
description:
"More views, more cycles powers, more pages features, new reports, and better dashboards are waiting to be unlocked.",
type: "PRO",
prices: [
{
id: `price_monthly_${EProductSubscriptionEnum.PRO}`,
unit_amount: 800,
recurring: "month",
currency: "usd",
workspace_amount: 800,
product: EProductSubscriptionEnum.PRO,
},
{
id: `price_yearly_${EProductSubscriptionEnum.PRO}`,
unit_amount: 7200,
recurring: "year",
currency: "usd",
workspace_amount: 7200,
product: EProductSubscriptionEnum.PRO,
},
],
payment_quantity: 1,
is_active: true,
},
[EProductSubscriptionEnum.BUSINESS]: {
id: EProductSubscriptionEnum.BUSINESS,
name: "Plane Business",
description:
"The earliest packaging of Business at $10 a seat a month billed annually, $12 a seat a month billed monthly for Plane Cloud",
type: "BUSINESS",
prices: [
{
id: `price_yearly_${EProductSubscriptionEnum.BUSINESS}`,
unit_amount: 15600,
recurring: "year",
currency: "usd",
workspace_amount: 15600,
product: EProductSubscriptionEnum.BUSINESS,
},
{
id: `price_monthly_${EProductSubscriptionEnum.BUSINESS}`,
unit_amount: 1500,
recurring: "month",
currency: "usd",
workspace_amount: 1500,
product: EProductSubscriptionEnum.BUSINESS,
},
],
payment_quantity: 1,
is_active: true,
},
[EProductSubscriptionEnum.ENTERPRISE]: {
id: EProductSubscriptionEnum.ENTERPRISE,
name: "Plane Enterprise",
description: "",
type: "ENTERPRISE",
prices: [
{
id: `price_yearly_${EProductSubscriptionEnum.ENTERPRISE}`,
unit_amount: 0,
recurring: "year",
currency: "usd",
workspace_amount: 0,
product: EProductSubscriptionEnum.ENTERPRISE,
},
{
id: `price_monthly_${EProductSubscriptionEnum.ENTERPRISE}`,
unit_amount: 0,
recurring: "month",
currency: "usd",
workspace_amount: 0,
product: EProductSubscriptionEnum.ENTERPRISE,
},
],
payment_quantity: 1,
is_active: false,
},
};
/**
* URL for the "Talk to Sales" page where users can contact sales team
*/
export const TALK_TO_SALES_URL = "https://plane.so/talk-to-sales";
/**
* Mapping of subscription types to their respective upgrade/redirection URLs based on billing frequency
* Used for self-hosted installations to redirect users to appropriate upgrade pages
*/
export const SUBSCRIPTION_REDIRECTION_URLS: Record<EProductSubscriptionEnum, Record<TBillingFrequency, string>> = {
[EProductSubscriptionEnum.FREE]: {
month: TALK_TO_SALES_URL,
year: TALK_TO_SALES_URL,
},
[EProductSubscriptionEnum.ONE]: {
month: TALK_TO_SALES_URL,
year: TALK_TO_SALES_URL,
},
[EProductSubscriptionEnum.PRO]: {
month: "https://app.plane.so/upgrade/pro/self-hosted?plan=month",
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",
},
[EProductSubscriptionEnum.ENTERPRISE]: {
month: TALK_TO_SALES_URL,
year: TALK_TO_SALES_URL,
},
};
/**
* Mapping of subscription types to their respective marketing webpage URLs
* Used to direct users to learn more about each plan's features and pricing
*/
export const SUBSCRIPTION_WEBPAGE_URLS: Record<EProductSubscriptionEnum, string> = {
[EProductSubscriptionEnum.FREE]: TALK_TO_SALES_URL,
[EProductSubscriptionEnum.ONE]: TALK_TO_SALES_URL,
[EProductSubscriptionEnum.PRO]: "https://plane.so/pro",
[EProductSubscriptionEnum.BUSINESS]: "https://plane.so/business",
[EProductSubscriptionEnum.ENTERPRISE]: "https://plane.so/business",
};

View File

@@ -0,0 +1,142 @@
import { EStartOfTheWeek } from "@plane/types";
export const PROFILE_SETTINGS = {
profile: {
key: "profile",
i18n_label: "profile.actions.profile",
href: `/settings/account`,
highlight: (pathname: string) => pathname === "/settings/account/",
},
security: {
key: "security",
i18n_label: "profile.actions.security",
href: `/settings/account/security`,
highlight: (pathname: string) => pathname === "/settings/account/security/",
},
activity: {
key: "activity",
i18n_label: "profile.actions.activity",
href: `/settings/account/activity`,
highlight: (pathname: string) => pathname === "/settings/account/activity/",
},
preferences: {
key: "preferences",
i18n_label: "profile.actions.preferences",
href: `/settings/account/preferences`,
highlight: (pathname: string) => pathname === "/settings/account/preferences",
},
notifications: {
key: "notifications",
i18n_label: "profile.actions.notifications",
href: `/settings/account/notifications`,
highlight: (pathname: string) => pathname === "/settings/account/notifications/",
},
"api-tokens": {
key: "api-tokens",
i18n_label: "profile.actions.api-tokens",
href: `/settings/account/api-tokens`,
highlight: (pathname: string) => pathname === "/settings/account/api-tokens/",
},
};
export const PROFILE_ACTION_LINKS: {
key: string;
i18n_label: string;
href: string;
highlight: (pathname: string) => boolean;
}[] = [
PROFILE_SETTINGS["profile"],
PROFILE_SETTINGS["security"],
PROFILE_SETTINGS["activity"],
PROFILE_SETTINGS["preferences"],
PROFILE_SETTINGS["notifications"],
PROFILE_SETTINGS["api-tokens"],
];
export const PROFILE_VIEWER_TAB = [
{
key: "summary",
route: "",
i18n_label: "profile.tabs.summary",
selected: "/",
},
];
export const PROFILE_ADMINS_TAB = [
{
key: "assigned",
route: "assigned",
i18n_label: "profile.tabs.assigned",
selected: "/assigned/",
},
{
key: "created",
route: "created",
i18n_label: "profile.tabs.created",
selected: "/created/",
},
{
key: "subscribed",
route: "subscribed",
i18n_label: "profile.tabs.subscribed",
selected: "/subscribed/",
},
{
key: "activity",
route: "activity",
i18n_label: "profile.tabs.activity",
selected: "/activity/",
},
];
export const PREFERENCE_OPTIONS: {
id: string;
title: string;
description: string;
}[] = [
{
id: "theme",
title: "theme",
description: "select_or_customize_your_interface_color_scheme",
},
{
id: "start_of_week",
title: "First day of the week",
description: "This will change how all calendars in your app look.",
},
];
/**
* @description The options for the start of the week
* @type {Array<{value: EStartOfTheWeek, label: string}>}
* @constant
*/
export const START_OF_THE_WEEK_OPTIONS = [
{
value: EStartOfTheWeek.SUNDAY,
label: "Sunday",
},
{
value: EStartOfTheWeek.MONDAY,
label: "Monday",
},
{
value: EStartOfTheWeek.TUESDAY,
label: "Tuesday",
},
{
value: EStartOfTheWeek.WEDNESDAY,
label: "Wednesday",
},
{
value: EStartOfTheWeek.THURSDAY,
label: "Thursday",
},
{
value: EStartOfTheWeek.FRIDAY,
label: "Friday",
},
{
value: EStartOfTheWeek.SATURDAY,
label: "Saturday",
},
];

View File

@@ -0,0 +1,160 @@
// plane imports
import { IProject, TProjectAppliedDisplayFilterKeys, TProjectOrderByOptions } from "@plane/types";
// local imports
import { RANDOM_EMOJI_CODES } from "./emoji";
export type TNetworkChoiceIconKey = "Lock" | "Globe2";
export type TNetworkChoice = {
key: 0 | 2;
labelKey: string;
i18n_label: string;
description: string;
iconKey: TNetworkChoiceIconKey;
};
export const NETWORK_CHOICES: TNetworkChoice[] = [
{
key: 0,
labelKey: "Private",
i18n_label: "workspace_projects.network.private.title",
description: "workspace_projects.network.private.description", //"Accessible only by invite",
iconKey: "Lock",
},
{
key: 2,
labelKey: "Public",
i18n_label: "workspace_projects.network.public.title",
description: "workspace_projects.network.public.description", //"Anyone in the workspace except Guests can join",
iconKey: "Globe2",
},
];
export const GROUP_CHOICES = {
backlog: {
key: "backlog",
i18n_label: "workspace_projects.state.backlog",
},
unstarted: {
key: "unstarted",
i18n_label: "workspace_projects.state.unstarted",
},
started: {
key: "started",
i18n_label: "workspace_projects.state.started",
},
completed: {
key: "completed",
i18n_label: "workspace_projects.state.completed",
},
cancelled: {
key: "cancelled",
i18n_label: "workspace_projects.state.cancelled",
},
};
export const PROJECT_AUTOMATION_MONTHS = [
{ i18n_label: "workspace_projects.common.months_count", value: 1 },
{ i18n_label: "workspace_projects.common.months_count", value: 3 },
{ i18n_label: "workspace_projects.common.months_count", value: 6 },
{ i18n_label: "workspace_projects.common.months_count", value: 9 },
{ i18n_label: "workspace_projects.common.months_count", value: 12 },
];
export const PROJECT_UNSPLASH_COVERS = [
"https://images.unsplash.com/photo-1531045535792-b515d59c3d1f?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=870&q=80",
"https://images.unsplash.com/photo-1693027407934-e3aa8a54c7ae?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=870&q=80",
"https://images.unsplash.com/photo-1518837695005-2083093ee35b?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=870&q=80",
"https://images.unsplash.com/photo-1464925257126-6450e871c667?auto=format&fit=crop&q=80&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&w=870&q=80",
"https://images.unsplash.com/photo-1606768666853-403c90a981ad?auto=format&fit=crop&q=80&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&w=870&q=80",
"https://images.unsplash.com/photo-1627556592933-ffe99c1cd9eb?auto=format&fit=crop&q=80&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&w=870&q=80",
"https://images.unsplash.com/photo-1643330683233-ff2ac89b002c?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=870&q=80",
"https://images.unsplash.com/photo-1542202229-7d93c33f5d07?auto=format&fit=crop&q=80&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&w=870&q=80",
"https://images.unsplash.com/photo-1511497584788-876760111969?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=870&q=80",
"https://images.unsplash.com/photo-1475738972911-5b44ce984c42?auto=format&fit=crop&q=80&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&w=870&q=80",
"https://images.unsplash.com/photo-1418065460487-3e41a6c84dc5?auto=format&fit=crop&q=80&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&w=870&q=80",
"https://images.unsplash.com/photo-1673393058808-50e9baaf4d2c?auto=format&fit=crop&q=80&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&w=870&q=80",
"https://images.unsplash.com/photo-1696643830146-44a8755f1905?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=870&q=80",
"https://images.unsplash.com/photo-1693868769698-6c7440636a09?auto=format&fit=crop&q=80&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&w=870&q=80",
"https://images.unsplash.com/photo-1691230995681-480d86cbc135?auto=format&fit=crop&q=80&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&w=870&q=80",
"https://images.unsplash.com/photo-1675351066828-6fc770b90dd2?auto=format&fit=crop&q=80&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&w=870&q=80",
];
export const PROJECT_ORDER_BY_OPTIONS: {
key: TProjectOrderByOptions;
i18n_label: string;
}[] = [
{
key: "sort_order",
i18n_label: "workspace_projects.sort.manual",
},
{
key: "name",
i18n_label: "workspace_projects.sort.name",
},
{
key: "created_at",
i18n_label: "workspace_projects.sort.created_at",
},
{
key: "members_length",
i18n_label: "workspace_projects.sort.members_length",
},
];
export const PROJECT_DISPLAY_FILTER_OPTIONS: {
key: TProjectAppliedDisplayFilterKeys;
i18n_label: string;
}[] = [
{
key: "my_projects",
i18n_label: "workspace_projects.scope.my_projects",
},
{
key: "archived_projects",
i18n_label: "workspace_projects.scope.archived_projects",
},
];
export const PROJECT_ERROR_MESSAGES = {
permissionError: {
i18n_title: "workspace_projects.error.permission",
i18n_message: undefined,
},
cycleDeleteError: {
i18n_title: "error",
i18n_message: "workspace_projects.error.cycle_delete",
},
moduleDeleteError: {
i18n_title: "error",
i18n_message: "workspace_projects.error.module_delete",
},
issueDeleteError: {
i18n_title: "error",
i18n_message: "workspace_projects.error.issue_delete",
},
};
export const DEFAULT_PROJECT_FORM_VALUES: Partial<IProject> = {
cover_image_url: PROJECT_UNSPLASH_COVERS[Math.floor(Math.random() * PROJECT_UNSPLASH_COVERS.length)],
description: "",
logo_props: {
in_use: "emoji",
emoji: {
value: RANDOM_EMOJI_CODES[Math.floor(Math.random() * RANDOM_EMOJI_CODES.length)],
},
},
identifier: "",
name: "",
network: 2,
project_lead: null,
};
export enum EProjectFeatureKey {
WORK_ITEMS = "work_items",
CYCLES = "cycles",
MODULES = "modules",
VIEWS = "views",
PAGES = "pages",
INTAKE = "intake",
}

View File

@@ -0,0 +1,2 @@
export * from "./operator-labels";
export * from "./option";

View File

@@ -0,0 +1,24 @@
import {
CORE_EQUALITY_OPERATOR,
CORE_COLLECTION_OPERATOR,
CORE_COMPARISON_OPERATOR,
TCoreSupportedOperators,
TCoreSupportedDateFilterOperators,
} from "@plane/types";
/**
* Core operator labels
*/
export const CORE_OPERATOR_LABELS_MAP: Record<TCoreSupportedOperators, string> = {
[CORE_EQUALITY_OPERATOR.EXACT]: "is",
[CORE_COLLECTION_OPERATOR.IN]: "is any of",
[CORE_COMPARISON_OPERATOR.RANGE]: "between",
} as const;
/**
* Core date-specific operator labels
*/
export const CORE_DATE_OPERATOR_LABELS_MAP: Record<TCoreSupportedDateFilterOperators, string> = {
[CORE_EQUALITY_OPERATOR.EXACT]: "is",
[CORE_COMPARISON_OPERATOR.RANGE]: "between",
} as const;

View File

@@ -0,0 +1,21 @@
import { TExtendedSupportedOperators } from "@plane/types";
/**
* Extended operator labels
*/
export const EXTENDED_OPERATOR_LABELS_MAP: Record<TExtendedSupportedOperators, string> = {} as const;
/**
* Extended date-specific operator labels
*/
export const EXTENDED_DATE_OPERATOR_LABELS_MAP: Record<TExtendedSupportedOperators, string> = {} as const;
/**
* Negated operator labels for all operators
*/
export const NEGATED_OPERATOR_LABELS_MAP: Record<never, string> = {} as const;
/**
* Negated date operator labels for all date operators
*/
export const NEGATED_DATE_OPERATOR_LABELS_MAP: Record<never, string> = {} as const;

View File

@@ -0,0 +1,36 @@
import { TAllAvailableOperatorsForDisplay, TAllAvailableDateFilterOperatorsForDisplay } from "@plane/types";
import { CORE_OPERATOR_LABELS_MAP, CORE_DATE_OPERATOR_LABELS_MAP } from "./core";
import {
EXTENDED_OPERATOR_LABELS_MAP,
EXTENDED_DATE_OPERATOR_LABELS_MAP,
NEGATED_OPERATOR_LABELS_MAP,
NEGATED_DATE_OPERATOR_LABELS_MAP,
} from "./extended";
/**
* Empty operator label for unselected state
*/
export const EMPTY_OPERATOR_LABEL = "--";
/**
* Complete operator labels mapping - combines core, extended, and negated labels
*/
export const OPERATOR_LABELS_MAP: Record<TAllAvailableOperatorsForDisplay, string> = {
...CORE_OPERATOR_LABELS_MAP,
...EXTENDED_OPERATOR_LABELS_MAP,
...NEGATED_OPERATOR_LABELS_MAP,
} as const;
/**
* Complete date operator labels mapping - combines core, extended, and negated labels
*/
export const DATE_OPERATOR_LABELS_MAP: Record<TAllAvailableDateFilterOperatorsForDisplay, string> = {
...CORE_DATE_OPERATOR_LABELS_MAP,
...EXTENDED_DATE_OPERATOR_LABELS_MAP,
...NEGATED_DATE_OPERATOR_LABELS_MAP,
} as const;
// -------- RE-EXPORTS --------
export * from "./core";
export * from "./extended";

View File

@@ -0,0 +1,83 @@
import { TExternalFilter } from "@plane/types";
/**
* Filter config options.
*/
export type TConfigOptions = Record<string, unknown>;
/**
* Default filter config options.
*/
export const DEFAULT_FILTER_CONFIG_OPTIONS: TConfigOptions = {};
/**
* Clear filter config.
*/
export type TClearFilterOptions = {
label?: string;
onFilterClear: () => void | Promise<void>;
isDisabled?: boolean;
};
/**
* Save view config.
*/
export type TSaveViewOptions<E extends TExternalFilter> = {
label?: string;
onViewSave: (expression: E) => void | Promise<void>;
isDisabled?: boolean;
};
/**
* Update view config.
*/
export type TUpdateViewOptions<E extends TExternalFilter> = {
label?: string;
hasAdditionalChanges?: boolean;
onViewUpdate: (expression: E) => void | Promise<void>;
isDisabled?: boolean;
};
/**
* Filter expression options.
*/
export type TExpressionOptions<E extends TExternalFilter> = {
clearFilterOptions?: TClearFilterOptions;
saveViewOptions?: TSaveViewOptions<E>;
updateViewOptions?: TUpdateViewOptions<E>;
};
/**
* Default filter expression options.
*/
export const DEFAULT_FILTER_EXPRESSION_OPTIONS: TExpressionOptions<TExternalFilter> = {};
/**
* Auto visibility options.
*/
export type TAutoVisibilityOptions =
| {
autoSetVisibility: true;
}
| {
autoSetVisibility: false;
isVisibleOnMount: boolean;
};
/**
* Default filter visibility options.
*/
export const DEFAULT_FILTER_VISIBILITY_OPTIONS: TAutoVisibilityOptions = {
autoSetVisibility: true,
};
/**
* Filter options.
* - expression: Filter expression options.
* - config: Filter config options.
*/
export type TFilterOptions<E extends TExternalFilter> = {
expression: Partial<TExpressionOptions<E>>;
config: Partial<TConfigOptions>;
visibility: TAutoVisibilityOptions;
};

View File

@@ -0,0 +1,52 @@
import { PROFILE_SETTINGS } from "./profile";
import { WORKSPACE_SETTINGS } from "./workspace";
export enum WORKSPACE_SETTINGS_CATEGORY {
ADMINISTRATION = "administration",
FEATURES = "features",
DEVELOPER = "developer",
}
export enum PROFILE_SETTINGS_CATEGORY {
YOUR_PROFILE = "your profile",
DEVELOPER = "developer",
}
export enum PROJECT_SETTINGS_CATEGORY {
PROJECTS = "projects",
}
export const WORKSPACE_SETTINGS_CATEGORIES = [
WORKSPACE_SETTINGS_CATEGORY.ADMINISTRATION,
WORKSPACE_SETTINGS_CATEGORY.FEATURES,
WORKSPACE_SETTINGS_CATEGORY.DEVELOPER,
];
export const PROFILE_SETTINGS_CATEGORIES = [
PROFILE_SETTINGS_CATEGORY.YOUR_PROFILE,
PROFILE_SETTINGS_CATEGORY.DEVELOPER,
];
export const PROJECT_SETTINGS_CATEGORIES = [PROJECT_SETTINGS_CATEGORY.PROJECTS];
export const GROUPED_WORKSPACE_SETTINGS = {
[WORKSPACE_SETTINGS_CATEGORY.ADMINISTRATION]: [
WORKSPACE_SETTINGS["general"],
WORKSPACE_SETTINGS["members"],
WORKSPACE_SETTINGS["billing-and-plans"],
WORKSPACE_SETTINGS["export"],
],
[WORKSPACE_SETTINGS_CATEGORY.FEATURES]: [],
[WORKSPACE_SETTINGS_CATEGORY.DEVELOPER]: [WORKSPACE_SETTINGS["webhooks"]],
};
export const GROUPED_PROFILE_SETTINGS = {
[PROFILE_SETTINGS_CATEGORY.YOUR_PROFILE]: [
PROFILE_SETTINGS["profile"],
PROFILE_SETTINGS["preferences"],
PROFILE_SETTINGS["notifications"],
PROFILE_SETTINGS["security"],
PROFILE_SETTINGS["activity"],
],
[PROFILE_SETTINGS_CATEGORY.DEVELOPER]: [PROFILE_SETTINGS["api-tokens"]],
};

View File

@@ -0,0 +1,2 @@
export const SIDEBAR_WIDTH = 250;
export const EXTENDED_SIDEBAR_WIDTH = 300;

View File

@@ -0,0 +1 @@
export const SPREADSHEET_SELECT_GROUP = "spreadsheet-issues";

View File

@@ -0,0 +1,108 @@
import { TStateGroups } from "@plane/types";
export type TDraggableData = {
groupKey: TStateGroups;
id: string;
};
export const STATE_GROUPS: {
[key in TStateGroups]: {
key: TStateGroups;
label: string;
defaultStateName: string;
color: string;
};
} = {
backlog: {
key: "backlog",
label: "Backlog",
defaultStateName: "Backlog",
color: "#d9d9d9",
},
unstarted: {
key: "unstarted",
label: "Unstarted",
defaultStateName: "Todo",
color: "#3f76ff",
},
started: {
key: "started",
label: "Started",
defaultStateName: "In Progress",
color: "#f59e0b",
},
completed: {
key: "completed",
label: "Completed",
defaultStateName: "Done",
color: "#16a34a",
},
cancelled: {
key: "cancelled",
label: "Canceled",
defaultStateName: "Cancelled",
color: "#dc2626",
},
};
export const ARCHIVABLE_STATE_GROUPS = [STATE_GROUPS.completed.key, STATE_GROUPS.cancelled.key];
export const COMPLETED_STATE_GROUPS = [STATE_GROUPS.completed.key];
export const PENDING_STATE_GROUPS = [
STATE_GROUPS.backlog.key,
STATE_GROUPS.unstarted.key,
STATE_GROUPS.started.key,
STATE_GROUPS.cancelled.key,
];
export const STATE_DISTRIBUTION = {
[STATE_GROUPS.backlog.key]: {
key: STATE_GROUPS.backlog.key,
issues: "backlog_issues",
points: "backlog_estimate_points",
},
[STATE_GROUPS.unstarted.key]: {
key: STATE_GROUPS.unstarted.key,
issues: "unstarted_issues",
points: "unstarted_estimate_points",
},
[STATE_GROUPS.started.key]: {
key: STATE_GROUPS.started.key,
issues: "started_issues",
points: "started_estimate_points",
},
[STATE_GROUPS.completed.key]: {
key: STATE_GROUPS.completed.key,
issues: "completed_issues",
points: "completed_estimate_points",
},
[STATE_GROUPS.cancelled.key]: {
key: STATE_GROUPS.cancelled.key,
issues: "cancelled_issues",
points: "cancelled_estimate_points",
},
};
export const PROGRESS_STATE_GROUPS_DETAILS = [
{
key: "completed_issues",
title: "Completed",
color: "#16A34A",
},
{
key: "started_issues",
title: "Started",
color: "#F59E0B",
},
{
key: "unstarted_issues",
title: "Unstarted",
color: "#3A3A3A",
},
{
key: "backlog_issues",
title: "Backlog",
color: "#A3A3A3",
},
];
export const DISPLAY_WORKFLOW_PRO_CTA = false;

View File

@@ -0,0 +1 @@
export const STICKIES_PER_PAGE = 30;

View File

@@ -0,0 +1,42 @@
export const ENTERPRISE_PLAN_FEATURES = [
"Private + managed deployments",
"GAC",
"LDAP support",
"Databases + Formulas",
"Unlimited and full Automation Flows",
"Full-suite professional services",
];
export const BUSINESS_PLAN_FEATURES = [
"Project Templates",
"Workflows + Approvals",
"Decision + Loops Automation",
"Custom Reports",
"Nested Pages",
"Intake Forms",
];
export const PRO_PLAN_FEATURES = [
"Dashboards + Reports",
"Full Time Tracking + Bulk Ops",
"Teamspaces",
"Trigger And Action",
"Wikis",
"Popular integrations",
];
export const ONE_PLAN_FEATURES = [
"OIDC + SAML for SSO",
"Active Cycles",
"Real-time collab + public views and page",
"Link pages in issues and vice-versa",
"Time-tracking + limited bulk ops",
"Docker, Kubernetes and more",
];
export const FREE_PLAN_UPGRADE_FEATURES = [
"OIDC + SAML for SSO",
"Time Tracking and Bulk Ops",
"Integrations",
"Public Views and Pages",
];

View File

@@ -0,0 +1,16 @@
export const DEFAULT_SWR_CONFIG = {
refreshWhenHidden: false,
revalidateIfStale: false,
revalidateOnFocus: false,
revalidateOnMount: true,
refreshInterval: 600000,
errorRetryCount: 3,
};
export const WEB_SWR_CONFIG = {
refreshWhenHidden: false,
revalidateIfStale: true,
revalidateOnFocus: true,
revalidateOnMount: true,
errorRetryCount: 3,
};

View File

@@ -0,0 +1,93 @@
export const ISSUE_FORM_TAB_INDICES = [
"name",
"description_html",
"feeling_lucky",
"state_id",
"priority",
"assignee_ids",
"label_ids",
"start_date",
"target_date",
"cycle_id",
"module_ids",
"estimate_point",
"parent_id",
"create_more",
"discard_button",
"draft_button",
"submit_button",
"project_id",
"remove_parent",
];
export const INTAKE_ISSUE_CREATE_FORM_TAB_INDICES = [
"name",
"description_html",
"state_id",
"priority",
"assignee_ids",
"label_ids",
"start_date",
"target_date",
"cycle_id",
"module_ids",
"estimate_point",
"parent_id",
"create_more",
"discard_button",
"submit_button",
];
export const CREATE_LABEL_TAB_INDICES = ["name", "color", "cancel", "submit"];
export const PROJECT_CREATE_TAB_INDICES = [
"name",
"identifier",
"description",
"network",
"lead",
"cancel",
"submit",
"close",
"cover_image",
"logo_props",
];
export const PROJECT_CYCLE_TAB_INDICES = ["name", "description", "date_range", "cancel", "submit", "project_id"];
export const PROJECT_MODULE_TAB_INDICES = [
"name",
"description",
"date_range",
"status",
"lead",
"member_ids",
"cancel",
"submit",
];
export const PROJECT_VIEW_TAB_INDICES = ["name", "description", "filters", "cancel", "submit"];
export const PROJECT_PAGE_TAB_INDICES = ["name", "public", "private", "cancel", "submit"];
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,
[ETabIndices.INTAKE_ISSUE_FORM]: INTAKE_ISSUE_CREATE_FORM_TAB_INDICES,
[ETabIndices.CREATE_LABEL]: CREATE_LABEL_TAB_INDICES,
[ETabIndices.PROJECT_CREATE]: PROJECT_CREATE_TAB_INDICES,
[ETabIndices.PROJECT_CYCLE]: PROJECT_CYCLE_TAB_INDICES,
[ETabIndices.PROJECT_MODULE]: PROJECT_MODULE_TAB_INDICES,
[ETabIndices.PROJECT_VIEW]: PROJECT_VIEW_TAB_INDICES,
[ETabIndices.PROJECT_PAGE]: PROJECT_PAGE_TAB_INDICES,
};

View File

@@ -0,0 +1,82 @@
export const THEMES = ["light", "dark", "light-contrast", "dark-contrast", "custom"];
export interface I_THEME_OPTION {
key: string;
value: string;
i18n_label: string;
type: string;
icon: {
border: string;
color1: string;
color2: string;
};
}
export const THEME_OPTIONS: I_THEME_OPTION[] = [
{
key: "system_preference",
value: "system",
i18n_label: "System preference",
type: "light",
icon: {
border: "#DEE2E6",
color1: "#FAFAFA",
color2: "#3F76FF",
},
},
{
key: "light",
value: "light",
i18n_label: "Light",
type: "light",
icon: {
border: "#DEE2E6",
color1: "#FAFAFA",
color2: "#3F76FF",
},
},
{
key: "dark",
value: "dark",
i18n_label: "Dark",
type: "dark",
icon: {
border: "#2E3234",
color1: "#191B1B",
color2: "#3C85D9",
},
},
{
key: "light_contrast",
value: "light-contrast",
i18n_label: "Light high contrast",
type: "light",
icon: {
border: "#000000",
color1: "#FFFFFF",
color2: "#3F76FF",
},
},
{
key: "dark_contrast",
value: "dark-contrast",
i18n_label: "Dark high contrast",
type: "dark",
icon: {
border: "#FFFFFF",
color1: "#030303",
color2: "#3A8BE9",
},
},
{
key: "custom",
value: "custom",
i18n_label: "Custom theme",
type: "light",
icon: {
border: "#FFC9C9",
color1: "#FFF7F7",
color2: "#FF5151",
},
},
];

View File

@@ -0,0 +1,59 @@
export enum EAuthenticationPageType {
STATIC = "STATIC",
NOT_AUTHENTICATED = "NOT_AUTHENTICATED",
AUTHENTICATED = "AUTHENTICATED",
}
export enum EInstancePageType {
PRE_SETUP = "PRE_SETUP",
POST_SETUP = "POST_SETUP",
}
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 enum EUserPermissionsLevel {
WORKSPACE = "WORKSPACE",
PROJECT = "PROJECT",
}
export type TUserPermissionsLevel = EUserPermissionsLevel;
export enum EUserPermissions {
ADMIN = 20,
MEMBER = 15,
GUEST = 5,
}
export type TUserPermissions = EUserPermissions;
export type TUserAllowedPermissionsObject = {
create: TUserPermissions[];
update: TUserPermissions[];
delete: TUserPermissions[];
read: TUserPermissions[];
};
export type TUserAllowedPermissions = {
workspace: {
[key: string]: Partial<TUserAllowedPermissionsObject>;
};
project: {
[key: string]: Partial<TUserAllowedPermissionsObject>;
};
};
export const USER_ALLOWED_PERMISSIONS: TUserAllowedPermissions = {
workspace: {
dashboard: {
read: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
},
},
project: {},
};

View File

@@ -0,0 +1,20 @@
import { EViewAccess } from "@plane/types";
export const VIEW_ACCESS_SPECIFIERS: {
key: EViewAccess;
i18n_label: string;
}[] = [
{ key: EViewAccess.PUBLIC, i18n_label: "common.access.public" },
{ key: EViewAccess.PRIVATE, i18n_label: "common.access.private" },
];
export const VIEW_SORTING_KEY_OPTIONS = [
{ key: "name", i18n_label: "project_view.sort_by.name" },
{ key: "created_at", i18n_label: "project_view.sort_by.created_at" },
{ key: "updated_at", i18n_label: "project_view.sort_by.updated_at" },
];
export const VIEW_SORT_BY_OPTIONS = [
{ key: "asc", i18n_label: "common.order_by.asc" },
{ key: "desc", i18n_label: "common.order_by.desc" },
];

View File

@@ -0,0 +1,6 @@
export enum EDraftIssuePaginationType {
INIT = "INIT",
NEXT = "NEXT",
PREV = "PREV",
CURRENT = "CURRENT",
}

View File

@@ -0,0 +1,348 @@
import { TStaticViewTypes, IWorkspaceSearchResults, EUserWorkspaceRoles } from "@plane/types";
export const ORGANIZATION_SIZE = ["Just myself", "2-10", "11-50", "51-200", "201-500", "500+"];
export const RESTRICTED_URLS = [
"404",
"accounts",
"api",
"create-workspace",
"god-mode",
"installations",
"invitations",
"onboarding",
"profile",
"spaces",
"workspace-invitations",
"password",
"flags",
"monitor",
"monitoring",
"ingest",
"plane-pro",
"plane-ultimate",
"enterprise",
"plane-enterprise",
"disco",
"silo",
"chat",
"calendar",
"drive",
"channels",
"upgrade",
"billing",
"sign-in",
"sign-up",
"signin",
"signup",
"config",
"live",
"admin",
"m",
"import",
"importers",
"integrations",
"integration",
"configuration",
"initiatives",
"initiative",
"config",
"workflow",
"workflows",
"epics",
"epic",
"story",
"mobile",
"dashboard",
"desktop",
"onload",
"real-time",
"one",
"pages",
"mobile",
"business",
"pro",
"settings",
"monitor",
"license",
"licenses",
"instances",
"instance",
];
export const WORKSPACE_SETTINGS = {
general: {
key: "general",
i18n_label: "workspace_settings.settings.general.title",
href: `/settings`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/`,
},
members: {
key: "members",
i18n_label: "workspace_settings.settings.members.title",
href: `/settings/members`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members/`,
},
"billing-and-plans": {
key: "billing-and-plans",
i18n_label: "workspace_settings.settings.billing_and_plans.title",
href: `/settings/billing`,
access: [EUserWorkspaceRoles.ADMIN],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/billing/`,
},
export: {
key: "export",
i18n_label: "workspace_settings.settings.exports.title",
href: `/settings/exports`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/exports/`,
},
webhooks: {
key: "webhooks",
i18n_label: "workspace_settings.settings.webhooks.title",
href: `/settings/webhooks`,
access: [EUserWorkspaceRoles.ADMIN],
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/webhooks/`,
},
};
export const WORKSPACE_SETTINGS_ACCESS = Object.fromEntries(
Object.entries(WORKSPACE_SETTINGS).map(([_, { href, access }]) => [href, access])
);
export const WORKSPACE_SETTINGS_LINKS: {
key: string;
i18n_label: string;
href: string;
access: EUserWorkspaceRoles[];
highlight: (pathname: string, baseUrl: string) => boolean;
}[] = [
WORKSPACE_SETTINGS["general"],
WORKSPACE_SETTINGS["members"],
WORKSPACE_SETTINGS["billing-and-plans"],
WORKSPACE_SETTINGS["export"],
WORKSPACE_SETTINGS["webhooks"],
];
export const ROLE = {
[EUserWorkspaceRoles.GUEST]: "Guest",
[EUserWorkspaceRoles.MEMBER]: "Member",
[EUserWorkspaceRoles.ADMIN]: "Admin",
};
export const ROLE_DETAILS = {
[EUserWorkspaceRoles.GUEST]: {
i18n_title: "role_details.guest.title",
i18n_description: "role_details.guest.description",
},
[EUserWorkspaceRoles.MEMBER]: {
i18n_title: "role_details.member.title",
i18n_description: "role_details.member.description",
},
[EUserWorkspaceRoles.ADMIN]: {
i18n_title: "role_details.admin.title",
i18n_description: "role_details.admin.description",
},
};
export const USER_ROLES = [
{
value: "Product / Project Manager",
i18n_label: "user_roles.product_or_project_manager",
},
{
value: "Development / Engineering",
i18n_label: "user_roles.development_or_engineering",
},
{
value: "Founder / Executive",
i18n_label: "user_roles.founder_or_executive",
},
{
value: "Freelancer / Consultant",
i18n_label: "user_roles.freelancer_or_consultant",
},
{ value: "Marketing / Growth", i18n_label: "user_roles.marketing_or_growth" },
{
value: "Sales / Business Development",
i18n_label: "user_roles.sales_or_business_development",
},
{
value: "Support / Operations",
i18n_label: "user_roles.support_or_operations",
},
{
value: "Student / Professor",
i18n_label: "user_roles.student_or_professor",
},
{ value: "Human Resources", i18n_label: "user_roles.human_resources" },
{ value: "Other", i18n_label: "user_roles.other" },
];
export const IMPORTERS_LIST = [
{
provider: "github",
type: "import",
i18n_title: "importer.github.title",
i18n_description: "importer.github.description",
},
{
provider: "jira",
type: "import",
i18n_title: "importer.jira.title",
i18n_description: "importer.jira.description",
},
];
export const EXPORTERS_LIST = [
{
provider: "csv",
type: "export",
i18n_title: "exporter.csv.title",
i18n_description: "exporter.csv.description",
},
{
provider: "xlsx",
type: "export",
i18n_title: "exporter.excel.title",
i18n_description: "exporter.csv.description",
},
{
provider: "json",
type: "export",
i18n_title: "exporter.json.title",
i18n_description: "exporter.csv.description",
},
];
export const DEFAULT_GLOBAL_VIEWS_LIST: {
key: TStaticViewTypes;
i18n_label: string;
}[] = [
{
key: "all-issues",
i18n_label: "default_global_view.all_issues",
},
{
key: "assigned",
i18n_label: "default_global_view.assigned",
},
{
key: "created",
i18n_label: "default_global_view.created",
},
{
key: "subscribed",
i18n_label: "default_global_view.subscribed",
},
];
export interface IWorkspaceSidebarNavigationItem {
key: string;
labelTranslationKey: string;
href: string;
access: EUserWorkspaceRoles[];
highlight: (pathname: string, url: string) => boolean;
}
export const WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS: Record<string, IWorkspaceSidebarNavigationItem> = {
views: {
key: "views",
labelTranslationKey: "views",
href: `/workspace-views/all-issues/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST],
highlight: (pathname: string, url: string) => pathname === url,
},
analytics: {
key: "analytics",
labelTranslationKey: "analytics",
href: `/analytics/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
highlight: (pathname: string, url: string) => pathname.includes(url),
},
drafts: {
key: "drafts",
labelTranslationKey: "drafts",
href: `/drafts/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
highlight: (pathname: string, url: string) => pathname.includes(url),
},
archives: {
key: "archives",
labelTranslationKey: "archives",
href: `/projects/archives/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
highlight: (pathname: string, url: string) => pathname.includes(url),
},
};
export const WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS_LINKS: IWorkspaceSidebarNavigationItem[] = [
WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["views"]!,
WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["analytics"]!,
WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["drafts"]!,
WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["archives"]!,
];
export const WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS: Record<string, IWorkspaceSidebarNavigationItem> = {
home: {
key: "home",
labelTranslationKey: "home.title",
href: `/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST],
highlight: (pathname: string, url: string) => pathname === url,
},
inbox: {
key: "inbox",
labelTranslationKey: "notification.label",
href: `/notifications/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST],
highlight: (pathname: string, url: string) => pathname.includes(url),
},
"your-work": {
key: "your_work",
labelTranslationKey: "your_work",
href: `/profile/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
highlight: (pathname: string, url: string) => pathname.includes(url),
},
projects: {
key: "projects",
labelTranslationKey: "projects",
href: `/projects/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST],
highlight: (pathname: string, url: string) => pathname === url,
},
};
export const WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS_LINKS: IWorkspaceSidebarNavigationItem[] = [
WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS["home"]!,
WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS["inbox"]!,
WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS["your-work"]!,
];
export const WORKSPACE_SIDEBAR_STATIC_PINNED_NAVIGATION_ITEMS_LINKS: IWorkspaceSidebarNavigationItem[] = [
WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS["projects"]!,
];
export const IS_FAVORITE_MENU_OPEN = "is_favorite_menu_open";
export const WORKSPACE_DEFAULT_SEARCH_RESULT: IWorkspaceSearchResults = {
results: {
workspace: [],
project: [],
issue: [],
cycle: [],
module: [],
issue_view: [],
page: [],
},
};
export const USE_CASES = [
"Plan and track product roadmaps",
"Manage engineering sprints",
"Coordinate cross-functional projects",
"Replace our current tool",
"Just exploring",
];

View File

@@ -0,0 +1,12 @@
{
"extends": "@plane/typescript-config/base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"sourceMap": true,
"strictNullChecks": true,
"allowSyntheticDefaultImports": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}

View File

@@ -0,0 +1,11 @@
import { defineConfig } from "tsdown";
export default defineConfig({
entry: ["src/index.ts"],
outDir: "dist",
format: ["esm", "cjs"],
exports: true,
dts: true,
clean: true,
sourcemap: false,
});

View File

@@ -0,0 +1,4 @@
node_modules
build/*
dist/*
out/*

View File

@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: ["@plane/eslint-config/library.js"],
};

View File

@@ -0,0 +1,2 @@
# Ignore generated build artifacts
dist/

View File

@@ -0,0 +1,5 @@
{
"printWidth": 120,
"tabWidth": 2,
"trailingComma": "es5"
}

View File

@@ -0,0 +1,95 @@
# @plane/decorators
A lightweight TypeScript decorator library for building Express.js controllers with a clean, declarative syntax.
## Features
- TypeScript-first design
- Decorators for HTTP methods (GET, POST, PUT, PATCH, DELETE)
- WebSocket support
- Middleware support
- No build step required - works directly with TypeScript files
## Installation
This package is part of the Plane workspace and can be used by adding it to your project's dependencies:
```json
{
"dependencies": {
"@plane/decorators": "workspace:*"
}
}
```
## Usage
### Basic REST Controller
```typescript
import { Controller, Get, Post, BaseController } from "@plane/decorators";
import { Router, Request, Response } from "express";
@Controller("/api/users")
class UserController extends BaseController {
@Get("/")
async getUsers(req: Request, res: Response) {
return res.json({ users: [] });
}
@Post("/")
async createUser(req: Request, res: Response) {
return res.json({ success: true });
}
}
// Register routes
const router = Router();
const userController = new UserController();
userController.registerRoutes(router);
```
### WebSocket Controller
```typescript
import { Controller, WebSocket, BaseWebSocketController } from "@plane/decorators";
import { Request } from "express";
import { WebSocket as WS } from "ws";
@Controller("/ws/chat")
class ChatController extends BaseWebSocketController {
@WebSocket("/")
handleConnection(ws: WS, req: Request) {
ws.on("message", (message) => {
ws.send(`Received: ${message}`);
});
}
}
// Register WebSocket routes
const router = require("express-ws")(app).router;
const chatController = new ChatController();
chatController.registerWebSocketRoutes(router);
```
## API Reference
### Decorators
- `@Controller(baseRoute: string)` - Class decorator for defining a base route
- `@Get(route: string)` - Method decorator for HTTP GET endpoints
- `@Post(route: string)` - Method decorator for HTTP POST endpoints
- `@Put(route: string)` - Method decorator for HTTP PUT endpoints
- `@Patch(route: string)` - Method decorator for HTTP PATCH endpoints
- `@Delete(route: string)` - Method decorator for HTTP DELETE endpoints
- `@WebSocket(route: string)` - Method decorator for WebSocket endpoints
- `@Middleware(middleware: RequestHandler)` - Method decorator for applying middleware
### Classes
- `BaseController` - Base class for REST controllers
- `BaseWebSocketController` - Base class for WebSocket controllers
## License
This project is licensed under the [GNU Affero General Public License v3.0](https://github.com/makeplane/plane/blob/master/LICENSE.txt).

View File

@@ -0,0 +1,37 @@
{
"name": "@plane/decorators",
"version": "0.1.0",
"description": "Controller and route decorators for Express.js applications",
"license": "AGPL-3.0",
"private": true,
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./package.json": "./package.json"
},
"scripts": {
"build": "tsdown",
"dev": "tsdown --watch",
"check:lint": "eslint . --max-warnings 2",
"check:types": "tsc --noEmit",
"check:format": "prettier --check \"**/*.{ts,tsx,md,json,css,scss}\"",
"fix:lint": "eslint . --fix",
"fix:format": "prettier --write \"**/*.{ts,tsx,md,json,css,scss}\"",
"clean": "rm -rf .turbo && rm -rf .next && rm -rf node_modules && rm -rf dist"
},
"devDependencies": {
"@plane/eslint-config": "workspace:*",
"@plane/typescript-config": "workspace:*",
"@types/express": "4.17.23",
"@types/node": "catalog:",
"@types/ws": "^8.5.10",
"reflect-metadata": "^0.2.2",
"tsdown": "catalog:",
"typescript": "catalog:"
},
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts"
}

View File

@@ -0,0 +1,102 @@
import type { RequestHandler, Router, Request } from "express";
import type { WebSocket } from "ws";
import "reflect-metadata";
export type HttpMethod = "get" | "post" | "put" | "delete" | "patch" | "options" | "head" | "ws";
type ControllerInstance = {
[key: string]: any;
};
export type ControllerConstructor = {
new (...args: any[]): ControllerInstance;
prototype: ControllerInstance;
};
export function registerController(
router: Router,
Controller: ControllerConstructor,
dependencies: unknown[] = []
): void {
// Create the controller instance with dependencies
const instance = new Controller(...dependencies);
// Determine if it's a WebSocket controller or REST controller by checking
// if it has any methods with the "ws" method metadata
const isWebsocket = Object.getOwnPropertyNames(Controller.prototype).some((methodName) => {
if (methodName === "constructor") return false;
return Reflect.getMetadata("method", instance, methodName) === "ws";
});
if (isWebsocket) {
// Register as WebSocket controller
// Pass the existing instance with dependencies to avoid creating a new instance without them
registerWebSocketController(router, Controller, instance);
} else {
// Register as REST controller with the existing instance
registerRestController(router, Controller, instance);
}
}
function registerRestController(
router: Router,
Controller: ControllerConstructor,
existingInstance?: ControllerInstance
): void {
const instance = existingInstance || new Controller();
const baseRoute = Reflect.getMetadata("baseRoute", Controller) as string;
Object.getOwnPropertyNames(Controller.prototype).forEach((methodName) => {
if (methodName === "constructor") return; // Skip the constructor
const method = Reflect.getMetadata("method", instance, methodName) as HttpMethod;
const route = Reflect.getMetadata("route", instance, methodName) as string;
const middlewares = (Reflect.getMetadata("middlewares", instance, methodName) as RequestHandler[]) || [];
if (method && route) {
const handler = instance[methodName] as unknown;
if (typeof handler === "function") {
if (method !== "ws") {
(router[method] as (path: string, ...handlers: RequestHandler[]) => void)(
`${baseRoute}${route}`,
...middlewares,
handler.bind(instance)
);
}
}
}
});
}
function registerWebSocketController(
router: Router,
Controller: ControllerConstructor,
existingInstance?: ControllerInstance
): void {
const instance = existingInstance || new Controller();
const baseRoute = Reflect.getMetadata("baseRoute", Controller) as string;
Object.getOwnPropertyNames(Controller.prototype).forEach((methodName) => {
if (methodName === "constructor") return; // Skip the constructor
const method = Reflect.getMetadata("method", instance, methodName) as string;
const route = Reflect.getMetadata("route", instance, methodName) as string;
if (method === "ws" && route) {
const handler = instance[methodName] as unknown;
if (typeof handler === "function" && "ws" in router && typeof router.ws === "function") {
router.ws(`${baseRoute}${route}`, (ws: WebSocket, req: Request) => {
try {
handler.call(instance, ws, req);
} catch (error) {
console.error(`WebSocket error in ${Controller.name}.${methodName}`, error);
ws.close(1011, error instanceof Error ? error.message : "Internal server error");
}
});
}
}
});
}

View File

@@ -0,0 +1,3 @@
export * from "./controller";
export * from "./rest";
export * from "./websocket";

View File

@@ -0,0 +1,51 @@
import "reflect-metadata";
import { RequestHandler } from "express";
// Define valid HTTP methods
type RestMethod = "get" | "post" | "put" | "patch" | "delete";
/**
* Controller decorator
* @param baseRoute
* @returns
*/
export function Controller(baseRoute: string = ""): ClassDecorator {
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
return function (target: Function) {
Reflect.defineMetadata("baseRoute", baseRoute, target);
};
}
/**
* Factory function to create HTTP method decorators
* @param method HTTP method to handle
* @returns Method decorator
*/
function createHttpMethodDecorator(method: RestMethod): (route: string) => MethodDecorator {
return function (route: string): MethodDecorator {
return function (target: object, propertyKey: string | symbol) {
Reflect.defineMetadata("method", method, target, propertyKey);
Reflect.defineMetadata("route", route, target, propertyKey);
};
};
}
// Export HTTP method decorators using the factory
export const Get = createHttpMethodDecorator("get");
export const Post = createHttpMethodDecorator("post");
export const Put = createHttpMethodDecorator("put");
export const Patch = createHttpMethodDecorator("patch");
export const Delete = createHttpMethodDecorator("delete");
/**
* Middleware decorator
* @param middleware
* @returns
*/
export function Middleware(middleware: RequestHandler): MethodDecorator {
return function (target: object, propertyKey: string | symbol) {
const middlewares = Reflect.getMetadata("middlewares", target, propertyKey) || [];
middlewares.push(middleware);
Reflect.defineMetadata("middlewares", middlewares, target, propertyKey);
};
}

View File

@@ -0,0 +1,13 @@
import "reflect-metadata";
/**
* WebSocket method decorator
* @param route
* @returns
*/
export function WebSocket(route: string): MethodDecorator {
return function (target: object, propertyKey: string | symbol) {
Reflect.defineMetadata("method", "ws", target, propertyKey);
Reflect.defineMetadata("route", route, target, propertyKey);
};
}

View File

@@ -0,0 +1,15 @@
{
"extends": "@plane/typescript-config/node-library.json",
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"lib": ["ES2020"],
"rootDir": ".",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["./src", "./*.ts"],
"exclude": ["dist", "build", "node_modules"]
}

View File

@@ -0,0 +1,11 @@
import { defineConfig } from "tsdown";
export default defineConfig({
entry: ["src/index.ts"],
outDir: "dist",
format: ["esm", "cjs"],
exports: true,
dts: true,
clean: true,
sourcemap: false,
});

View File

@@ -0,0 +1,4 @@
node_modules
build/*
dist/*
out/*

View File

@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: ["@plane/eslint-config/library.js"],
};

View File

@@ -0,0 +1,6 @@
.next
.vercel
.tubro
out/
dist/
build/

View File

@@ -0,0 +1,5 @@
{
"printWidth": 120,
"tabWidth": 2,
"trailingComma": "es5"
}

82
packages/editor/Readme.md Normal file
View File

@@ -0,0 +1,82 @@
# @plane/editor
## Description
The `@plane/editor` package serves as the foundation for our editor system. It provides the base functionality for our other editor packages, but it will not be used directly in any of the projects but only for extending other editors.
## Utilities
We provide a wide range of utilities for extending the core itself.
1. Merging classes and custom styling
2. Adding new extensions
3. Adding custom props
4. Base menu items, and their commands
This allows for extensive customization and flexibility in the Editors created using our `editor-core` package.
### Here's a detailed overview of what's exported
1. useEditor - A hook that you can use to extend the Plane editor.
| Prop | Type | Description |
| ------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `extensions` | `Extension[]` | An array of custom extensions you want to add into the editor to extend it's core features |
| `editorProps` | `EditorProps` | Extend the editor props by passing in a custom props object |
| `uploadFile` | `(file: File) => Promise<string>` | A function that handles file upload. It takes a file as input and handles the process of uploading that file. |
| `deleteFile` | `(assetUrlWithWorkspaceId: string) => Promise<any>` | A function that handles deleting an image. It takes the asset url from your bucket and handles the process of deleting that image. |
| `value` | `html string` | The initial content of the editor. |
| `debouncedUpdatesEnabled` | `boolean` | If set to true, the `onChange` event handler is debounced, meaning it will only be invoked after the specified delay (default 1500ms) once the user has stopped typing. |
| `onChange` | `(json: any, html: string) => void` | This function is invoked whenever the content of the editor changes. It is passed the new content in both JSON and HTML formats. |
| `setIsSubmitting` | `(isSubmitting: "submitting" \| "submitted" \| "saved") => void` | This function is called to update the submission status. |
| `setShouldShowAlert` | `(showAlert: boolean) => void` | This function is used to show or hide an alert in case of content not being "saved". |
| `forwardedRef` | `any` | Pass this in whenever you want to control the editor's state from an external component |
2. useReadOnlyEditor - A hook that can be used to extend a Read Only instance of the core editor.
| Prop | Type | Description |
| -------------- | ------------- | ------------------------------------------------------------------------------------------ |
| `value` | `string` | The initial content of the editor. |
| `forwardedRef` | `any` | Pass this in whenever you want to control the editor's state from an external component |
| `extensions` | `Extension[]` | An array of custom extensions you want to add into the editor to extend it's core features |
| `editorProps` | `EditorProps` | Extend the editor props by passing in a custom props object |
3. Items and Commands - H1, H2, H3, task list, quote, code block, etc's methods.
4. UI Wrappers
- `EditorContainer` - Wrap your Editor Container with this to apply base classes and styles.
- `EditorContentWrapper` - Use this to get Editor's Content and base menus.
5. Extending with Custom Styles
```ts
const customEditorClassNames = getEditorClassNames({
noBorder,
borderOnFocus,
customClassName,
});
```
## Core features
- **Content Trimming**: The Editors content is now automatically trimmed of empty line breaks from the start and end before submitting it to the backend. This ensures cleaner, more consistent data.
- **Value Cleaning**: The Editors value is cleaned at the editor core level, eliminating the need for additional validation before sending from our app. This results in cleaner code and less potential for errors.
- **Turbo Pipeline**: Added a turbo pipeline for both dev and build tasks for projects depending on the editor package.
## Base extensions included
- BulletList
- OrderedList
- Blockquote
- Code
- Gapcursor
- Link
- Image
- Basic Marks
- Underline
- TextStyle
- Color
- TaskList
- Markdown
- Table

View File

@@ -0,0 +1,102 @@
{
"name": "@plane/editor",
"version": "1.1.0",
"description": "Core Editor that powers Plane",
"license": "AGPL-3.0",
"private": true,
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.cts",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./lib": {
"import": "./dist/lib.js",
"require": "./dist/lib.cjs"
},
"./package.json": "./package.json",
"./styles.css": "./dist/styles/index.css",
"./styles": "./dist/styles/index.css"
},
"scripts": {
"build": "tsc && tsdown",
"dev": "tsdown --watch",
"check:lint": "eslint . --max-warnings 30",
"check:types": "tsc --noEmit",
"check:format": "prettier --check \"**/*.{ts,tsx,md,json,css,scss}\"",
"fix:lint": "eslint . --fix",
"fix:format": "prettier --write \"**/*.{ts,tsx,md,json,css,scss}\"",
"clean": "rm -rf .turbo && rm -rf .next && rm -rf node_modules && rm -rf dist"
},
"peerDependencies": {
"react": "catalog:",
"react-dom": "catalog:"
},
"dependencies": {
"@floating-ui/dom": "^1.7.1",
"@floating-ui/react": "^0.26.4",
"@headlessui/react": "^1.7.3",
"@hocuspocus/provider": "2.15.2",
"@plane/constants": "workspace:*",
"@plane/hooks": "workspace:*",
"@plane/types": "workspace:*",
"@plane/ui": "workspace:*",
"@plane/propel": "workspace:*",
"@plane/utils": "workspace:*",
"@tiptap/core": "catalog:",
"@tiptap/extension-blockquote": "^2.22.3",
"@tiptap/extension-character-count": "^2.22.3",
"@tiptap/extension-collaboration": "^2.22.3",
"@tiptap/extension-emoji": "^2.22.3",
"@tiptap/extension-image": "^2.22.3",
"@tiptap/extension-list-item": "^2.22.3",
"@tiptap/extension-mention": "^2.22.3",
"@tiptap/extension-placeholder": "^2.22.3",
"@tiptap/extension-task-item": "^2.22.3",
"@tiptap/extension-task-list": "^2.22.3",
"@tiptap/extension-text-align": "^2.22.3",
"@tiptap/extension-text-style": "^2.22.3",
"@tiptap/extension-underline": "^2.22.3",
"@tiptap/html": "catalog:",
"@tiptap/pm": "^2.22.3",
"@tiptap/react": "^2.22.3",
"@tiptap/starter-kit": "^2.22.3",
"@tiptap/suggestion": "^2.22.3",
"emoji-regex": "^10.3.0",
"highlight.js": "^11.8.0",
"is-emoji-supported": "^0.0.5",
"jsx-dom-cjs": "^8.0.3",
"linkifyjs": "^4.3.2",
"lowlight": "^3.0.0",
"lucide-react": "catalog:",
"prosemirror-codemark": "^0.4.2",
"tippy.js": "^6.3.7",
"tiptap-markdown": "^0.8.10",
"uuid": "catalog:",
"y-indexeddb": "^9.0.12",
"y-prosemirror": "^1.2.15",
"y-protocols": "^1.0.6",
"yjs": "^13.6.20"
},
"devDependencies": {
"@plane/eslint-config": "workspace:*",
"@plane/tailwind-config": "workspace:*",
"@plane/typescript-config": "workspace:*",
"@types/node": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"postcss": "^8.4.38",
"tsdown": "catalog:",
"typescript": "catalog:"
},
"keywords": [
"editor",
"rich-text",
"markdown",
"nextjs",
"react"
]
}

View File

@@ -0,0 +1,9 @@
// If you want to use other PostCSS plugins, see the following:
// https://tailwindcss.com/docs/using-with-preprocessors
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

@@ -0,0 +1,12 @@
import { type Editor } from "@tiptap/core";
import type { ReactElement } from "react";
import type { IEditorPropsExtended } from "@/types";
export type DocumentEditorSideEffectsProps = {
editor: Editor;
id: string;
updatePageProperties?: unknown;
extendedEditorProps?: IEditorPropsExtended;
};
export const DocumentEditorSideEffects = (_props: DocumentEditorSideEffectsProps): ReactElement | null => null;

View File

@@ -0,0 +1,14 @@
import { Editor } from "@tiptap/core";
import { LinkViewContainer } from "@/components/editors/link-view-container";
export const LinkContainer = ({
editor,
containerRef,
}: {
editor: Editor;
containerRef: React.RefObject<HTMLDivElement>;
}) => (
<>
<LinkViewContainer editor={editor} containerRef={containerRef} />
</>
);

View File

@@ -0,0 +1,6 @@
// helpers
import { TAssetMetaDataRecord } from "@/helpers/assets";
// local imports
import { ADDITIONAL_EXTENSIONS } from "./extensions";
export const ADDITIONAL_ASSETS_META_DATA_RECORD: Partial<Record<ADDITIONAL_EXTENSIONS, TAssetMetaDataRecord>> = {};

View File

@@ -0,0 +1 @@
export enum ADDITIONAL_EXTENSIONS {}

View File

@@ -0,0 +1,22 @@
// plane imports
import { ADDITIONAL_EXTENSIONS, CORE_EXTENSIONS } from "@plane/utils";
// plane editor imports
import type { ExtensionFileSetStorageKey } from "@/plane-editor/types/storage";
export type NodeFileMapType = Partial<
Record<
CORE_EXTENSIONS | ADDITIONAL_EXTENSIONS,
{
fileSetName: ExtensionFileSetStorageKey;
}
>
>;
export const NODE_FILE_MAP: NodeFileMapType = {
[CORE_EXTENSIONS.IMAGE]: {
fileSetName: "deletedImageSet",
},
[CORE_EXTENSIONS.CUSTOM_IMAGE]: {
fileSetName: "deletedImageSet",
},
};

View File

@@ -0,0 +1,13 @@
import type { Extensions } from "@tiptap/core";
// types
import type { IEditorProps } from "@/types";
export type TCoreAdditionalExtensionsProps = Pick<
IEditorProps,
"disabledExtensions" | "flaggedExtensions" | "fileHandler" | "extendedEditorProps"
>;
export const CoreEditorAdditionalExtensions = (props: TCoreAdditionalExtensionsProps): Extensions => {
const {} = props;
return [];
};

View File

@@ -0,0 +1 @@
export * from "./extensions";

View File

@@ -0,0 +1,3 @@
import { Extensions } from "@tiptap/core";
export const CoreEditorAdditionalExtensionsWithoutProps: Extensions = [];

View File

@@ -0,0 +1,37 @@
import type { HocuspocusProvider } from "@hocuspocus/provider";
import type { AnyExtension } from "@tiptap/core";
import { SlashCommands } from "@/extensions";
// types
import type { IEditorProps, TExtensions, TUserDetails } from "@/types";
export type TDocumentEditorAdditionalExtensionsProps = Pick<
IEditorProps,
"disabledExtensions" | "flaggedExtensions" | "fileHandler" | "extendedEditorProps"
> & {
isEditable: boolean;
provider?: HocuspocusProvider;
userDetails: TUserDetails;
};
export type TDocumentEditorAdditionalExtensionsRegistry = {
isEnabled: (disabledExtensions: TExtensions[], flaggedExtensions: TExtensions[]) => boolean;
getExtension: (props: TDocumentEditorAdditionalExtensionsProps) => AnyExtension;
};
const extensionRegistry: TDocumentEditorAdditionalExtensionsRegistry[] = [
{
isEnabled: (disabledExtensions) => !disabledExtensions.includes("slash-commands"),
getExtension: ({ disabledExtensions, flaggedExtensions }) =>
SlashCommands({ disabledExtensions, flaggedExtensions }),
},
];
export const DocumentEditorAdditionalExtensions = (props: TDocumentEditorAdditionalExtensionsProps) => {
const { disabledExtensions, flaggedExtensions } = props;
const documentExtensions = extensionRegistry
.filter((config) => config.isEnabled(disabledExtensions, flaggedExtensions))
.map((config) => config.getExtension(props));
return documentExtensions;
};

View File

@@ -0,0 +1,3 @@
export * from "./core";
export * from "./document-extensions";
export * from "./slash-commands";

View File

@@ -0,0 +1,42 @@
import { AnyExtension, Extensions } from "@tiptap/core";
// extensions
import { SlashCommands } from "@/extensions/slash-commands/root";
// types
import { IEditorProps, TExtensions } from "@/types";
export type TRichTextEditorAdditionalExtensionsProps = Pick<
IEditorProps,
"disabledExtensions" | "flaggedExtensions" | "fileHandler" | "extendedEditorProps"
>;
/**
* Registry entry configuration for extensions
*/
export type TRichTextEditorAdditionalExtensionsRegistry = {
/** Determines if the extension should be enabled based on disabled extensions */
isEnabled: (disabledExtensions: TExtensions[], flaggedExtensions: 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, flaggedExtensions }) =>
SlashCommands({
disabledExtensions,
flaggedExtensions,
}),
},
];
export const RichTextEditorAdditionalExtensions = (props: TRichTextEditorAdditionalExtensionsProps) => {
const { disabledExtensions, flaggedExtensions } = props;
const extensions: Extensions = extensionRegistry
.filter((config) => config.isEnabled(disabledExtensions, flaggedExtensions))
.map((config) => config.getExtension(props))
.filter((extension): extension is AnyExtension => extension !== undefined);
return extensions;
};

View File

@@ -0,0 +1,12 @@
// extensions
import type { TSlashCommandAdditionalOption } from "@/extensions";
// types
import type { IEditorProps } from "@/types";
type Props = Pick<IEditorProps, "disabledExtensions" | "flaggedExtensions">;
export const coreEditorAdditionalSlashCommandOptions = (props: Props): TSlashCommandAdditionalOption[] => {
const {} = props;
const options: TSlashCommandAdditionalOption[] = [];
return options;
};

View File

@@ -0,0 +1,19 @@
/**
* @description function to extract all additional assets from HTML content
* @param htmlContent
* @returns {string[]} array of additional asset sources
*/
export const extractAdditionalAssetsFromHTMLContent = (_htmlContent: string): string[] => [];
/**
* @description function to replace additional assets in HTML content with new IDs
* @param props
* @returns {string} HTML content with replaced additional assets
*/
export const replaceAdditionalAssetsInHTMLContent = (props: {
htmlContent: string;
assetMap: Record<string, string>;
}): string => {
const { htmlContent } = props;
return htmlContent;
};

View File

@@ -0,0 +1 @@
export type TAdditionalEditorAsset = never;

View File

@@ -0,0 +1 @@
export type TExtendedFileHandler = object;

View File

@@ -0,0 +1,11 @@
export type IEditorExtensionOptions = unknown;
export type IEditorPropsExtended = unknown;
export type ICollaborativeDocumentEditorPropsExtended = unknown;
export type TExtendedEditorCommands = never;
export type TExtendedCommandExtraProps = unknown;
export type TExtendedEditorRefApi = unknown;

View File

@@ -0,0 +1,3 @@
export * from "./issue-embed";
export * from "./editor-extended";
export * from "./config";

View File

@@ -0,0 +1,17 @@
export type TEmbedConfig = {
issue?: TIssueEmbedConfig;
};
export type TReadOnlyEmbedConfig = TEmbedConfig;
export type TIssueEmbedConfig = {
widgetCallback: ({
issueId,
projectId,
workspaceSlug,
}: {
issueId: string;
projectId: string | undefined;
workspaceSlug: string | undefined;
}) => React.ReactNode;
};

View File

@@ -0,0 +1,4 @@
// extensions
import type { ImageExtensionStorage } from "@/extensions/image";
export type ExtensionFileSetStorageKey = Extract<keyof ImageExtensionStorage, "deletedImageSet">;

View File

@@ -0,0 +1 @@
export type TAdditionalActiveDropbarExtensions = never;

View File

@@ -0,0 +1,116 @@
import React from "react";
// plane imports
import { cn } from "@plane/utils";
// components
import { PageRenderer } from "@/components/editors";
// constants
import { DEFAULT_DISPLAY_CONFIG } from "@/constants/config";
// helpers
import { getEditorClassNames } from "@/helpers/common";
// hooks
import { useCollaborativeEditor } from "@/hooks/use-collaborative-editor";
// constants
import { DocumentEditorSideEffects } from "@/plane-editor/components/document-editor-side-effects";
// types
import type { EditorRefApi, ICollaborativeDocumentEditorProps } from "@/types";
const CollaborativeDocumentEditor: React.FC<ICollaborativeDocumentEditorProps> = (props) => {
const {
aiHandler,
bubbleMenuEnabled = true,
containerClassName,
documentLoaderClassName,
extensions = [],
disabledExtensions,
displayConfig = DEFAULT_DISPLAY_CONFIG,
editable,
editorClassName = "",
editorProps,
extendedEditorProps,
fileHandler,
flaggedExtensions,
forwardedRef,
handleEditorReady,
id,
dragDropEnabled = true,
isTouchDevice,
mentionHandler,
onAssetChange,
onChange,
onEditorFocus,
onTransaction,
placeholder,
realtimeConfig,
serverHandler,
tabIndex,
user,
extendedDocumentEditorProps,
} = props;
// use document editor
const { editor, hasServerConnectionFailed, hasServerSynced } = useCollaborativeEditor({
disabledExtensions,
editable,
editorClassName,
editorProps,
extendedEditorProps,
extensions,
fileHandler,
flaggedExtensions,
forwardedRef,
handleEditorReady,
id,
dragDropEnabled,
isTouchDevice,
mentionHandler,
onAssetChange,
onChange,
onEditorFocus,
onTransaction,
placeholder,
realtimeConfig,
serverHandler,
tabIndex,
user,
extendedDocumentEditorProps,
});
const editorContainerClassNames = getEditorClassNames({
noBorder: true,
borderOnFocus: false,
containerClassName,
});
if (!editor) return null;
return (
<>
<DocumentEditorSideEffects editor={editor} id={id} extendedEditorProps={extendedEditorProps} />
<PageRenderer
aiHandler={aiHandler}
bubbleMenuEnabled={bubbleMenuEnabled}
displayConfig={displayConfig}
documentLoaderClassName={documentLoaderClassName}
editor={editor}
editorContainerClassName={cn(editorContainerClassNames, "document-editor")}
id={id}
isTouchDevice={!!isTouchDevice}
isLoading={!hasServerSynced && !hasServerConnectionFailed}
tabIndex={tabIndex}
flaggedExtensions={flaggedExtensions}
disabledExtensions={disabledExtensions}
extendedDocumentEditorProps={extendedDocumentEditorProps}
/>
</>
);
};
const CollaborativeDocumentEditorWithRef = React.forwardRef<EditorRefApi, ICollaborativeDocumentEditorProps>(
(props, ref) => (
<CollaborativeDocumentEditor {...props} forwardedRef={ref as React.MutableRefObject<EditorRefApi | null>} />
)
);
CollaborativeDocumentEditorWithRef.displayName = "CollaborativeDocumentEditorWithRef";
export { CollaborativeDocumentEditorWithRef };

View File

@@ -0,0 +1,107 @@
import { Extensions } from "@tiptap/core";
import { forwardRef, MutableRefObject, useMemo } from "react";
// plane imports
import { cn } from "@plane/utils";
// components
import { PageRenderer } from "@/components/editors";
// constants
import { DEFAULT_DISPLAY_CONFIG } from "@/constants/config";
// extensions
import { HeadingListExtension, SideMenuExtension } from "@/extensions";
// helpers
import { getEditorClassNames } from "@/helpers/common";
// hooks
import { useEditor } from "@/hooks/use-editor";
// plane editor extensions
import { DocumentEditorAdditionalExtensions } from "@/plane-editor/extensions";
// types
import { EditorRefApi, IDocumentEditorProps } from "@/types";
const DocumentEditor = (props: IDocumentEditorProps) => {
const {
bubbleMenuEnabled = false,
containerClassName,
disabledExtensions,
displayConfig = DEFAULT_DISPLAY_CONFIG,
editable,
editorClassName = "",
extendedEditorProps,
fileHandler,
flaggedExtensions,
forwardedRef,
id,
isTouchDevice,
handleEditorReady,
mentionHandler,
onChange,
user,
value,
} = props;
const extensions: Extensions = useMemo(() => {
const additionalExtensions: Extensions = [];
additionalExtensions.push(
SideMenuExtension({
aiEnabled: !disabledExtensions?.includes("ai"),
dragDropEnabled: true,
}),
HeadingListExtension,
...DocumentEditorAdditionalExtensions({
disabledExtensions,
extendedEditorProps,
flaggedExtensions,
isEditable: editable,
fileHandler,
userDetails: user ?? {
id: "",
name: "",
color: "",
},
})
);
return additionalExtensions;
}, [disabledExtensions, editable, extendedEditorProps, fileHandler, flaggedExtensions, user]);
const editor = useEditor({
disabledExtensions,
editable,
editorClassName,
enableHistory: true,
extendedEditorProps,
extensions,
fileHandler,
flaggedExtensions,
forwardedRef,
handleEditorReady,
id,
initialValue: value,
mentionHandler,
onChange,
});
const editorContainerClassName = getEditorClassNames({
containerClassName,
});
if (!editor) return null;
return (
<PageRenderer
bubbleMenuEnabled={bubbleMenuEnabled}
displayConfig={displayConfig}
editor={editor}
editorContainerClassName={cn(editorContainerClassName, "document-editor")}
id={id}
flaggedExtensions={flaggedExtensions}
disabledExtensions={disabledExtensions}
isTouchDevice={!!isTouchDevice}
/>
);
};
const DocumentEditorWithRef = forwardRef<EditorRefApi, IDocumentEditorProps>((props, ref) => (
<DocumentEditor {...props} forwardedRef={ref as MutableRefObject<EditorRefApi | null>} />
));
DocumentEditorWithRef.displayName = "DocumentEditorWithRef";
export { DocumentEditorWithRef };

View File

@@ -0,0 +1,4 @@
export * from "./collaborative-editor";
export * from "./editor";
export * from "./loader";
export * from "./page-renderer";

Some files were not shown because too many files have changed in this diff Show More