base
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
.next/
|
||||
.react-router/
|
||||
.turbo/
|
||||
.vite/
|
||||
build/
|
||||
dist/
|
||||
node_modules/
|
||||
out/
|
||||
pnpm-lock.yaml
|
||||
storybook-static/
|
||||
@@ -0,0 +1,537 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import { CommentKind } from "ast-types/gen/kinds";
|
||||
import {
|
||||
API,
|
||||
FileInfo,
|
||||
Options,
|
||||
JSCodeshift,
|
||||
TSTypeReference,
|
||||
Identifier,
|
||||
BlockStatement,
|
||||
Expression,
|
||||
Pattern,
|
||||
SpreadElement,
|
||||
JSXNamespacedName,
|
||||
ASTNode,
|
||||
Node,
|
||||
FunctionDeclaration,
|
||||
TSType,
|
||||
VariableDeclarator,
|
||||
ArrowFunctionExpression,
|
||||
FunctionExpression,
|
||||
} from "jscodeshift";
|
||||
|
||||
const COMPONENT_TYPE_NAMES = new Set([
|
||||
"FC",
|
||||
"FunctionComponent",
|
||||
"VFC",
|
||||
"VoidFunctionComponent",
|
||||
]);
|
||||
|
||||
const COMPONENT_NAME_PATTERN = /^[A-Z]/;
|
||||
|
||||
function isReactComponentType(typeReference: TSTypeReference, j: JSCodeshift) {
|
||||
const typeName = typeReference.typeName;
|
||||
|
||||
if (!typeName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (j.Identifier.check(typeName)) {
|
||||
return COMPONENT_TYPE_NAMES.has(typeName.name);
|
||||
}
|
||||
|
||||
if (
|
||||
j.TSQualifiedName.check(typeName) &&
|
||||
j.Identifier.check(typeName.left) &&
|
||||
j.Identifier.check(typeName.right)
|
||||
) {
|
||||
return (
|
||||
typeName.left.name === "React" &&
|
||||
COMPONENT_TYPE_NAMES.has(typeName.right.name)
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isComponentNameIdentifier(identifier: Identifier | null | undefined) {
|
||||
if (!identifier) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must start with uppercase
|
||||
if (!COMPONENT_NAME_PATTERN.test(identifier.name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ignore CONSTANT_CASE (all uppercase)
|
||||
if (
|
||||
identifier.name.toUpperCase() === identifier.name &&
|
||||
identifier.name.length > 1
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function addComments(target: Node, comments: CommentKind[]) {
|
||||
if (!comments || comments.length === 0) {
|
||||
return;
|
||||
}
|
||||
target.comments ||= [];
|
||||
target.comments.push(...comments);
|
||||
}
|
||||
|
||||
function copyOuterComments(source: Node, target: Node, j: JSCodeshift) {
|
||||
if (!j.Node.check(source) || !j.Node.check(target) || !source.comments) {
|
||||
return;
|
||||
}
|
||||
const outerComments = source.comments.filter((c) => c.leading || c.trailing);
|
||||
addComments(target, outerComments);
|
||||
}
|
||||
|
||||
function ensureParamType(
|
||||
param: Pattern,
|
||||
propsType: TSType | null | undefined,
|
||||
j: JSCodeshift
|
||||
) {
|
||||
if (!j.Pattern.check(param)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!("typeAnnotation" in param)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!propsType) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (j.TSTypeReference.check(propsType) && propsType.typeName) {
|
||||
param.typeAnnotation = j.tsTypeAnnotation(
|
||||
propsType.typeParameters
|
||||
? j.tsTypeReference(propsType.typeName, propsType.typeParameters)
|
||||
: j.tsTypeReference(propsType.typeName)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (j.TSType.check(propsType)) {
|
||||
// @ts-expect-error: jscodeshift types are too strict here
|
||||
param.typeAnnotation = j.tsTypeAnnotation(propsType);
|
||||
}
|
||||
}
|
||||
|
||||
function toBlockBody(j: JSCodeshift, body: BlockStatement | Expression) {
|
||||
if (j.BlockStatement.check(body)) {
|
||||
return body;
|
||||
}
|
||||
|
||||
// @ts-expect-error: jscodeshift types are too strict here
|
||||
const returnStatement = j.returnStatement(body);
|
||||
|
||||
return j.blockStatement([returnStatement]);
|
||||
}
|
||||
|
||||
function isFunction(
|
||||
node: Node,
|
||||
j: JSCodeshift
|
||||
): node is ArrowFunctionExpression | FunctionExpression {
|
||||
return (
|
||||
j.ArrowFunctionExpression.check(node) || j.FunctionExpression.check(node)
|
||||
);
|
||||
}
|
||||
|
||||
function extractArrowFunction(
|
||||
init: Expression | SpreadElement | JSXNamespacedName,
|
||||
j: JSCodeshift
|
||||
): ArrowFunctionExpression | FunctionExpression | undefined {
|
||||
if (isFunction(init, j)) {
|
||||
return init;
|
||||
}
|
||||
|
||||
// If it's a CallExpression like observer(() => {}), extract the arrow function
|
||||
if (j.CallExpression.check(init)) {
|
||||
const firstArg = init.arguments?.[0];
|
||||
if (firstArg && isFunction(firstArg, j)) {
|
||||
return firstArg;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function extractPropsTypeFromWrapper(
|
||||
init: Expression | SpreadElement | JSXNamespacedName,
|
||||
j: JSCodeshift
|
||||
) {
|
||||
// If it's a CallExpression like observer<React.FC<Props>>((props) => {})
|
||||
// Extract the Props type from React.FC<Props>
|
||||
if (!j.CallExpression.check(init)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!("typeParameters" in init)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const typeParameters = init.typeParameters;
|
||||
if (!j.TSTypeParameterInstantiation.check(typeParameters)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const typeParam = typeParameters.params?.[0];
|
||||
|
||||
if (!typeParam) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
j.TSTypeReference.check(typeParam) &&
|
||||
isReactComponentType(typeParam, j)
|
||||
) {
|
||||
// Extract the generic type from React.FC<PropsType>
|
||||
return typeParam.typeParameters?.params?.[0];
|
||||
}
|
||||
|
||||
// Check for memo<Props>
|
||||
const callee = init.callee;
|
||||
let isMemo = false;
|
||||
if (j.Identifier.check(callee) && callee.name === "memo") {
|
||||
isMemo = true;
|
||||
} else if (
|
||||
j.MemberExpression.check(callee) &&
|
||||
j.Identifier.check(callee.property) &&
|
||||
callee.property.name === "memo"
|
||||
) {
|
||||
isMemo = true;
|
||||
}
|
||||
|
||||
if (isMemo) {
|
||||
// For memo<Props>, the first type parameter is the props type
|
||||
// @ts-expect-error: jscodeshift types are too strict here
|
||||
return typeParam;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function isReactForwardRef(
|
||||
init: Expression | SpreadElement | JSXNamespacedName,
|
||||
j: JSCodeshift
|
||||
) {
|
||||
if (!j.CallExpression.check(init)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const callee = init.callee;
|
||||
|
||||
// Check for React.forwardRef
|
||||
if (
|
||||
j.MemberExpression.check(callee) &&
|
||||
j.Identifier.check(callee.object) &&
|
||||
j.Identifier.check(callee.property)
|
||||
) {
|
||||
return (
|
||||
callee.object.name === "React" && callee.property.name === "forwardRef"
|
||||
);
|
||||
}
|
||||
|
||||
// Check for forwardRef (imported directly)
|
||||
if (j.Identifier.check(callee)) {
|
||||
return callee.name === "forwardRef";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function extractForwardRefTypes(
|
||||
init: Expression | SpreadElement | JSXNamespacedName,
|
||||
j: JSCodeshift
|
||||
) {
|
||||
if (!isReactForwardRef(init, j)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!j.CallExpression.check(init) || !("typeParameters" in init)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const typeParameters = init.typeParameters;
|
||||
|
||||
// If no type parameters, we still want to apply default empty object for props
|
||||
if (
|
||||
!j.TSTypeParameterInstantiation.check(typeParameters) ||
|
||||
typeParameters.params.length === 0
|
||||
) {
|
||||
return; // Let the default props type handling take care of it
|
||||
}
|
||||
|
||||
const typeParams = typeParameters.params;
|
||||
|
||||
// React.forwardRef<ElementType, PropsType>
|
||||
// If PropsType is not specified, use Record<string, unknown> to avoid ESLint errors
|
||||
const [elementType] = typeParams;
|
||||
|
||||
if (!elementType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const propsType =
|
||||
typeParams.length >= 2 && typeParams[1]
|
||||
? typeParams[1]
|
||||
: j.tsTypeReference(
|
||||
j.identifier("Record"),
|
||||
j.tsTypeParameterInstantiation([
|
||||
j.tsStringKeyword(),
|
||||
j.tsUnknownKeyword(),
|
||||
])
|
||||
);
|
||||
|
||||
// Create React.ForwardedRef<ElementType> for the ref parameter
|
||||
const refType = j.tsTypeReference(
|
||||
j.tsQualifiedName(j.identifier("React"), j.identifier("ForwardedRef")),
|
||||
j.tsTypeParameterInstantiation([elementType])
|
||||
);
|
||||
|
||||
return { propsType, refType };
|
||||
}
|
||||
|
||||
function isEmptyObjectType(type: TSType, j: JSCodeshift) {
|
||||
return j.TSTypeLiteral.check(type) && type.members.length === 0;
|
||||
}
|
||||
|
||||
function convertToFunction(
|
||||
j: JSCodeshift,
|
||||
declaration: VariableDeclarator,
|
||||
init: Expression | SpreadElement | JSXNamespacedName,
|
||||
propsType: TSType | null | undefined
|
||||
) {
|
||||
if (!j.Identifier.check(declaration.id)) {
|
||||
throw new Error("Declaration id must be an identifier");
|
||||
}
|
||||
const componentName = declaration.id.name;
|
||||
const arrowFn = extractArrowFunction(init, j);
|
||||
|
||||
if (!arrowFn) {
|
||||
throw new Error("Expected ArrowFunctionExpression or FunctionExpression");
|
||||
}
|
||||
|
||||
const params = arrowFn.params;
|
||||
const body = toBlockBody(j, arrowFn.body);
|
||||
|
||||
const newFunction = j.functionDeclaration(
|
||||
j.identifier(componentName),
|
||||
params,
|
||||
body
|
||||
);
|
||||
|
||||
// Check if this is React.forwardRef and extract types for props and ref
|
||||
const forwardRefTypes = extractForwardRefTypes(init, j);
|
||||
|
||||
if (forwardRefTypes) {
|
||||
// Apply props type to first parameter
|
||||
const [firstParam, secondParam] = newFunction.params;
|
||||
if (j.Pattern.check(firstParam) && "typeAnnotation" in firstParam) {
|
||||
ensureParamType(firstParam, forwardRefTypes.propsType, j);
|
||||
}
|
||||
// Apply ref type to second parameter
|
||||
if (j.Pattern.check(secondParam) && "typeAnnotation" in secondParam) {
|
||||
ensureParamType(secondParam, forwardRefTypes.refType, j);
|
||||
}
|
||||
} else if (newFunction.params.length > 0) {
|
||||
const [firstParam] = newFunction.params;
|
||||
if (firstParam) {
|
||||
ensureParamType(firstParam, propsType, j);
|
||||
}
|
||||
} else if (propsType && !isEmptyObjectType(propsType, j)) {
|
||||
// If there are no params but a non-empty propsType exists, add _props parameter
|
||||
const propsParam = j.identifier("_props");
|
||||
ensureParamType(propsParam, propsType, j);
|
||||
newFunction.params.push(propsParam);
|
||||
}
|
||||
|
||||
if (arrowFn.returnType) {
|
||||
newFunction.returnType = arrowFn.returnType;
|
||||
}
|
||||
|
||||
// Preserve type parameters (generics) from arrow function
|
||||
if (arrowFn.typeParameters) {
|
||||
newFunction.typeParameters = arrowFn.typeParameters;
|
||||
}
|
||||
|
||||
newFunction.async = arrowFn.async;
|
||||
newFunction.generator = arrowFn.generator;
|
||||
|
||||
return newFunction;
|
||||
}
|
||||
|
||||
function containsJsx(j: JSCodeshift, body: ASTNode) {
|
||||
return (
|
||||
j(body).find(j.JSXElement).paths().length > 0 ||
|
||||
j(body).find(j.JSXFragment).paths().length > 0
|
||||
);
|
||||
}
|
||||
|
||||
function toFunctionExpression(
|
||||
j: JSCodeshift,
|
||||
declaration: FunctionDeclaration
|
||||
) {
|
||||
const expression = j.functionExpression(
|
||||
declaration.id,
|
||||
declaration.params,
|
||||
declaration.body,
|
||||
declaration.generator,
|
||||
declaration.async
|
||||
);
|
||||
expression.returnType = declaration.returnType;
|
||||
expression.typeParameters = declaration.typeParameters;
|
||||
return expression;
|
||||
}
|
||||
|
||||
export default function transform(file: FileInfo, api: API, options: Options) {
|
||||
const baseJ = api.jscodeshift;
|
||||
const j =
|
||||
typeof baseJ.withParser === "function" ? baseJ.withParser("tsx") : baseJ;
|
||||
const root = j(file.source);
|
||||
|
||||
root
|
||||
.find(j.VariableDeclaration)
|
||||
.filter((path) => {
|
||||
const [firstDeclaration] = path.node.declarations;
|
||||
|
||||
if (!j.VariableDeclarator.check(firstDeclaration)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
!j.Identifier.check(firstDeclaration.id) ||
|
||||
!isComponentNameIdentifier(firstDeclaration.id)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const init = firstDeclaration.init;
|
||||
|
||||
if (!init) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const functionToCheck = extractArrowFunction(init, j);
|
||||
|
||||
if (!functionToCheck) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.path && !file.path.endsWith(".tsx")) {
|
||||
if (!containsJsx(j, functionToCheck)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.forEach((path) => {
|
||||
const [firstDeclaration] = path.node.declarations;
|
||||
if (!j.VariableDeclarator.check(firstDeclaration)) {
|
||||
return;
|
||||
}
|
||||
const init = firstDeclaration.init;
|
||||
|
||||
if (!init) {
|
||||
return;
|
||||
}
|
||||
|
||||
let typeAnnotation: ASTNode | null | undefined;
|
||||
if (j.Identifier.check(firstDeclaration.id)) {
|
||||
typeAnnotation = firstDeclaration.id.typeAnnotation?.typeAnnotation;
|
||||
}
|
||||
|
||||
// Try to get props type from variable type annotation first
|
||||
let propsType: TSType | undefined = undefined;
|
||||
|
||||
if (
|
||||
j.TSTypeReference.check(typeAnnotation) &&
|
||||
isReactComponentType(typeAnnotation, j)
|
||||
) {
|
||||
propsType = typeAnnotation.typeParameters?.params?.[0];
|
||||
}
|
||||
|
||||
// If no props type from variable annotation, try to extract from wrapper's type parameters
|
||||
if (!propsType) {
|
||||
propsType = extractPropsTypeFromWrapper(init, j);
|
||||
}
|
||||
|
||||
const newFunction = convertToFunction(
|
||||
j,
|
||||
firstDeclaration,
|
||||
init,
|
||||
propsType
|
||||
);
|
||||
|
||||
const originalNode = path.node;
|
||||
|
||||
// Check if init is wrapped in a call expression (e.g., observer(...))
|
||||
const hasWrapper = j.CallExpression.check(init);
|
||||
|
||||
if (hasWrapper) {
|
||||
// Preserve the wrapper by keeping it as a const assignment
|
||||
// e.g., export const Foo = observer(() => {}) becomes export const Foo = observer(function Foo() {...})
|
||||
|
||||
// Convert function declaration to function expression for wrapping
|
||||
const functionExpression = toFunctionExpression(j, newFunction);
|
||||
|
||||
const wrappedFunction = j.callExpression(init.callee, [
|
||||
functionExpression,
|
||||
...init.arguments.slice(1),
|
||||
]);
|
||||
|
||||
if (!j.Identifier.check(firstDeclaration.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newDeclarator = j.variableDeclarator(
|
||||
j.identifier(firstDeclaration.id.name),
|
||||
wrappedFunction
|
||||
);
|
||||
|
||||
const newVarDecl = j.variableDeclaration("const", [newDeclarator]);
|
||||
|
||||
// Copy comments from original declaration to new function
|
||||
copyOuterComments(originalNode, newVarDecl, j);
|
||||
|
||||
j(path).replaceWith(newVarDecl);
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy outer comments from original declaration to new function
|
||||
copyOuterComments(originalNode, newFunction, j);
|
||||
|
||||
// Copy comments from VariableDeclarator (e.g. export /* comment */ const Foo)
|
||||
if (firstDeclaration.comments) {
|
||||
addComments(newFunction, firstDeclaration.comments);
|
||||
}
|
||||
|
||||
// Copy comments from arrow function
|
||||
if (init.comments) {
|
||||
addComments(newFunction, init.comments);
|
||||
}
|
||||
|
||||
j(path).replaceWith(newFunction);
|
||||
});
|
||||
|
||||
const quote = options.quote ?? '"';
|
||||
|
||||
const source = root.toSource({
|
||||
quote,
|
||||
});
|
||||
|
||||
return source;
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
# jscodeshift Instructions
|
||||
|
||||
## API Reference
|
||||
|
||||
### Core API
|
||||
|
||||
#### `jscodeshift`
|
||||
|
||||
The main function that returns the jscodeshift instance.
|
||||
|
||||
- **Parameters**: `source` (String)
|
||||
- **Example**:
|
||||
```javascript
|
||||
const j = jscodeshift(sourceCode);
|
||||
```
|
||||
|
||||
### Node Traversal APIs
|
||||
|
||||
#### `find`
|
||||
|
||||
Finds nodes that match the provided type.
|
||||
|
||||
- **Parameters**: `type` (String or Function)
|
||||
- **Example**:
|
||||
```javascript
|
||||
const variableDeclarations = j.find(j.VariableDeclaration);
|
||||
```
|
||||
|
||||
#### `findImportDeclarations`
|
||||
|
||||
Finds all ImportDeclarations optionally filtered by name.
|
||||
|
||||
- **Parameters**: `sourcePath` (String)
|
||||
- **Example**:
|
||||
```javascript
|
||||
const routerImports = j.findImportDeclarations("react-router-dom");
|
||||
```
|
||||
|
||||
#### `closestScope`
|
||||
|
||||
Finds the closest enclosing scope of a node.
|
||||
|
||||
- **Example**:
|
||||
```javascript
|
||||
const closestScopes = j.find(j.Identifier).closestScope();
|
||||
```
|
||||
|
||||
#### `closest`
|
||||
|
||||
Finds the nearest parent node that matches the specified type.
|
||||
|
||||
- **Parameters**: `type` (String or Function)
|
||||
- **Example**:
|
||||
```javascript
|
||||
const closestFunction = j.find(j.Identifier).closest(j.FunctionDeclaration);
|
||||
```
|
||||
|
||||
#### `getVariableDeclarators`
|
||||
|
||||
Retrieves variable declarators from the current collection.
|
||||
|
||||
- **Parameters**: `callback` (Function)
|
||||
- **Example**:
|
||||
```javascript
|
||||
const variableDeclarators = j
|
||||
.find(j.Identifier)
|
||||
.getVariableDeclarators((path) => path.value.name);
|
||||
```
|
||||
|
||||
#### `findVariableDeclarators`
|
||||
|
||||
Finds variable declarators by name.
|
||||
|
||||
- **Parameters**: `name` (String)
|
||||
- **Example**:
|
||||
```javascript
|
||||
const variableDeclarators = j.findVariableDeclarators("a");
|
||||
```
|
||||
|
||||
#### `filter`
|
||||
|
||||
Filters nodes based on a predicate function.
|
||||
|
||||
- **Parameters**: `predicate` (Function)
|
||||
- **Example**:
|
||||
```javascript
|
||||
const constDeclarations = j
|
||||
.find(j.VariableDeclaration)
|
||||
.filter((path) => path.node.kind === "const");
|
||||
```
|
||||
|
||||
#### `forEach`
|
||||
|
||||
Iterates over each node in the collection.
|
||||
|
||||
- **Parameters**: `callback` (Function)
|
||||
- **Example**:
|
||||
```javascript
|
||||
j.find(j.VariableDeclaration).forEach((path) => {
|
||||
console.log(path.node);
|
||||
});
|
||||
```
|
||||
|
||||
#### `some`
|
||||
|
||||
Checks if at least one element in the collection passes the test.
|
||||
|
||||
- **Parameters**: `callback` (Function)
|
||||
- **Example**:
|
||||
```javascript
|
||||
const hasVariableA = root
|
||||
.find(j.VariableDeclarator)
|
||||
.some((path) => path.node.id.name === "a");
|
||||
```
|
||||
|
||||
#### `every`
|
||||
|
||||
Checks if all elements in the collection pass the test.
|
||||
|
||||
- **Parameters**: `callback` (Function)
|
||||
- **Example**:
|
||||
```javascript
|
||||
const allAreConst = root
|
||||
.find(j.VariableDeclaration)
|
||||
.every((path) => path.node.kind === "const");
|
||||
```
|
||||
|
||||
#### `map`
|
||||
|
||||
Maps each node in the collection to a new value.
|
||||
|
||||
- **Parameters**: `callback` (Function)
|
||||
- **Example**:
|
||||
```javascript
|
||||
const variableNames = j
|
||||
.find(j.VariableDeclaration)
|
||||
.map((path) => path.node.declarations.map((decl) => decl.id.name));
|
||||
```
|
||||
|
||||
#### `size`
|
||||
|
||||
Returns the number of nodes in the collection.
|
||||
|
||||
- **Example**:
|
||||
```javascript
|
||||
const numberOfNodes = j.find(j.VariableDeclaration).size();
|
||||
```
|
||||
|
||||
#### `length`
|
||||
|
||||
Returns the number of elements in the collection.
|
||||
|
||||
- **Example**:
|
||||
```javascript
|
||||
const varCount = root.find(j.VariableDeclarator).length;
|
||||
```
|
||||
|
||||
#### `nodes`
|
||||
|
||||
Returns the AST nodes in the collection.
|
||||
|
||||
- **Example**:
|
||||
```javascript
|
||||
const nodes = j.find(j.VariableDeclaration).nodes();
|
||||
```
|
||||
|
||||
#### `paths`
|
||||
|
||||
Returns the paths of the found nodes.
|
||||
|
||||
- **Example**:
|
||||
```javascript
|
||||
const paths = j.find(j.VariableDeclaration).paths();
|
||||
```
|
||||
|
||||
#### `getAST`
|
||||
|
||||
Returns the root AST node of the collection.
|
||||
|
||||
- **Example**:
|
||||
```javascript
|
||||
const ast = root.getAST();
|
||||
```
|
||||
|
||||
#### `get`
|
||||
|
||||
Gets the first node in the collection.
|
||||
|
||||
- **Example**:
|
||||
```javascript
|
||||
const firstVariableDeclaration = j.find(j.VariableDeclaration).get();
|
||||
```
|
||||
|
||||
#### `at`
|
||||
|
||||
Navigates to a specific path in the AST.
|
||||
|
||||
- **Parameters**: `index` (Number)
|
||||
- **Example**:
|
||||
```javascript
|
||||
const secondVariableDeclaration = j.find(j.VariableDeclaration).at(1);
|
||||
```
|
||||
|
||||
#### `getTypes`
|
||||
|
||||
Returns the set of node types present in the collection.
|
||||
|
||||
- **Example**:
|
||||
```javascript
|
||||
const types = root.find(j.VariableDeclarator).getTypes();
|
||||
```
|
||||
|
||||
#### `isOfType`
|
||||
|
||||
Checks if the node in the collection is of a specific type.
|
||||
|
||||
- **Parameters**: `type` (String)
|
||||
- **Example**:
|
||||
```javascript
|
||||
const isVariableDeclarator = root
|
||||
.find(j.VariableDeclarator)
|
||||
.at(0)
|
||||
.isOfType("VariableDeclarator");
|
||||
```
|
||||
|
||||
### Node Transformation APIs
|
||||
|
||||
#### `replaceWith`
|
||||
|
||||
Replaces the current node(s) with a new node.
|
||||
|
||||
- **Parameters**: `newNode` (Node or Function)
|
||||
- **Example**:
|
||||
```javascript
|
||||
j.find(j.Identifier).replaceWith((path) =>
|
||||
j.identifier(path.node.name.toUpperCase())
|
||||
);
|
||||
```
|
||||
|
||||
#### `insertBefore`
|
||||
|
||||
Inserts a node before the current node.
|
||||
|
||||
- **Parameters**: `newNode` (Node)
|
||||
- **Example**:
|
||||
```javascript
|
||||
j.find(j.FunctionDeclaration).insertBefore(
|
||||
j.expressionStatement(j.stringLiteral("Inserted before"))
|
||||
);
|
||||
```
|
||||
|
||||
#### `insertAfter`
|
||||
|
||||
Inserts a node after the current node.
|
||||
|
||||
- **Parameters**: `newNode` (Node)
|
||||
- **Example**:
|
||||
```javascript
|
||||
j.find(j.FunctionDeclaration).insertAfter(
|
||||
j.expressionStatement(j.stringLiteral("Inserted after"))
|
||||
);
|
||||
```
|
||||
|
||||
#### `remove`
|
||||
|
||||
Removes the current node(s).
|
||||
|
||||
- **Example**:
|
||||
```javascript
|
||||
j.find(j.VariableDeclaration).remove();
|
||||
```
|
||||
|
||||
#### `renameTo`
|
||||
|
||||
Renames the nodes in the collection to a new name.
|
||||
|
||||
- **Parameters**: `newName` (String)
|
||||
- **Example**:
|
||||
```javascript
|
||||
root.find(j.Identifier, { name: "a" }).renameTo("x");
|
||||
```
|
||||
|
||||
#### `toSource`
|
||||
|
||||
Converts the transformed AST back to source code.
|
||||
|
||||
- **Parameters**: `options` (Object)
|
||||
- **Example**:
|
||||
```javascript
|
||||
const transformedSource = j.toSource({ quote: "single" });
|
||||
```
|
||||
|
||||
## AST Grammar
|
||||
|
||||
jscodeshift provides 278 node types which are mapped to their corresponding node type in `ast-types`.
|
||||
|
||||
### Common Node Types
|
||||
|
||||
- **AnyTypeAnnotation**: A type annotation representing any type.
|
||||
- **ArrayExpression**: Represents an array literal.
|
||||
- **ArrayPattern**: A pattern that matches an array from a destructuring assignment.
|
||||
- **ArrayTypeAnnotation**: A type annotation for arrays.
|
||||
- **ArrowFunctionExpression**: An arrow function expression.
|
||||
- **AssignmentExpression**: Represents an assignment expression.
|
||||
- **AssignmentPattern**: A pattern that matches an assignment from a destructuring assignment.
|
||||
- **AwaitExpression**: Represents an await expression.
|
||||
- **BigIntLiteral**: A literal representing a big integer.
|
||||
- **BinaryExpression**: Represents a binary expression.
|
||||
- **BlockStatement**: Represents a block statement.
|
||||
- **BooleanLiteral**: A literal representing a boolean value.
|
||||
- **BreakStatement**: Represents a break statement.
|
||||
- **CallExpression**: Represents a call expression.
|
||||
- **CatchClause**: Represents a catch clause in a try statement.
|
||||
- **ClassDeclaration**: Represents a class declaration.
|
||||
- **ClassExpression**: Represents a class expression.
|
||||
- **ClassMethod**: Represents a method of a class.
|
||||
- **ClassProperty**: Represents a property of a class.
|
||||
- **Comment**: Represents a comment in the code.
|
||||
- **ConditionalExpression**: Represents a conditional expression (ternary).
|
||||
- **ContinueStatement**: Represents a continue statement.
|
||||
- **DebuggerStatement**: Represents a debugger statement.
|
||||
- **Declaration**: Represents a declaration in the code.
|
||||
- **DoWhileStatement**: Represents a do…while statement.
|
||||
- **ExportAllDeclaration**: Represents an export all declaration.
|
||||
- **ExportDeclaration**: Represents an export declaration.
|
||||
- **ExportDefaultDeclaration**: Represents an export default declaration.
|
||||
- **ExportNamedDeclaration**: Represents a named export declaration.
|
||||
- **ExpressionStatement**: Represents an expression statement.
|
||||
- **File**: Represents a file in the AST.
|
||||
- **ForInStatement**: Represents a for-in statement.
|
||||
- **ForOfStatement**: Represents a for-of statement.
|
||||
- **ForStatement**: Represents a for statement.
|
||||
- **FunctionDeclaration**: Represents a function declaration.
|
||||
- **FunctionExpression**: Represents a function expression.
|
||||
- **Identifier**: Represents an identifier.
|
||||
- **IfStatement**: Represents an if statement.
|
||||
- **ImportDeclaration**: Represents an import declaration.
|
||||
- **ImportDefaultSpecifier**: Represents a default import specifier.
|
||||
- **ImportNamespaceSpecifier**: Represents a namespace import specifier.
|
||||
- **ImportSpecifier**: Represents an import specifier.
|
||||
- **InterfaceDeclaration**: Represents an interface declaration.
|
||||
- **JSXAttribute**: Represents an attribute in a JSX element.
|
||||
- **JSXElement**: Represents a JSX element.
|
||||
- **JSXExpressionContainer**: Represents an expression container in JSX.
|
||||
- **JSXFragment**: Represents a JSX fragment.
|
||||
- **JSXIdentifier**: Represents an identifier in JSX.
|
||||
- **JSXText**: Represents text in JSX.
|
||||
- **Literal**: Represents a literal value.
|
||||
- **LogicalExpression**: Represents a logical expression.
|
||||
- **MemberExpression**: Represents a member expression.
|
||||
- **MethodDefinition**: Represents a method definition.
|
||||
- **NewExpression**: Represents a new expression.
|
||||
- **ObjectExpression**: Represents an object expression.
|
||||
- **ObjectPattern**: Represents an object pattern for destructuring.
|
||||
- **ObjectProperty**: Represents a property in an object.
|
||||
- **Program**: Represents the entire program.
|
||||
- **Property**: Represents a property in an object.
|
||||
- **ReturnStatement**: Represents a return statement.
|
||||
- **SpreadElement**: Represents a spread element in an array or function call.
|
||||
- **StringLiteral**: Represents a string literal.
|
||||
- **SwitchCase**: Represents a case in a switch statement.
|
||||
- **SwitchStatement**: Represents a switch statement.
|
||||
- **TemplateLiteral**: Represents a template literal.
|
||||
- **ThisExpression**: Represents the `this` expression.
|
||||
- **ThrowStatement**: Represents a throw statement.
|
||||
- **TryStatement**: Represents a try statement.
|
||||
- **TSAnyKeyword**: Represents the TypeScript `any` keyword.
|
||||
- **TSArrayType**: Represents a TypeScript array type.
|
||||
- **TSAsExpression**: Represents a TypeScript as-expression.
|
||||
- **TSBooleanKeyword**: Represents the TypeScript `boolean` keyword.
|
||||
- **TSDeclareFunction**: Represents a TypeScript function declaration.
|
||||
- **TSEnumDeclaration**: Represents a TypeScript enum declaration.
|
||||
- **TSInterfaceDeclaration**: Represents a TypeScript interface declaration.
|
||||
- **TSNumberKeyword**: Represents the TypeScript `number` keyword.
|
||||
- **TSStringKeyword**: Represents the TypeScript `string` keyword.
|
||||
- **TSTypeAliasDeclaration**: Represents a TypeScript type alias declaration.
|
||||
- **TSTypeAnnotation**: Represents a TypeScript type annotation.
|
||||
- **TSTypeReference**: Represents a type reference in TypeScript.
|
||||
- **TSUnionType**: Represents a union type in TypeScript.
|
||||
- **UnaryExpression**: Represents a unary expression.
|
||||
- **VariableDeclaration**: Represents a variable declaration.
|
||||
- **VariableDeclarator**: Represents a variable declarator.
|
||||
- **WhileStatement**: Represents a while statement.
|
||||
|
||||
For a complete list and detailed structure of each node, refer to the [AST Grammar documentation](https://jscodeshift.com/build/ast-grammar/).
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@plane/codemods",
|
||||
"version": "1.3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"function-declaration": "jscodeshift -t ./function-declaration.ts --extensions=tsx --parser=tsx ../../apps/*/app ../../apps/*/ce ../../apps/*/core ../../apps/*/ee ../../apps/*/helpers ../../packages/*/src --ignore-pattern='**/node_modules/**' --ignore-pattern='**/dist/**' --ignore-pattern='**/build/**' --ignore-pattern='**/*.d.ts'",
|
||||
"remove-directives": "jscodeshift -t ./remove-directives.ts --extensions=ts,tsx --parser=tsx ../../apps/*/app ../../apps/*/ce ../../apps/*/core ../../apps/*/ee ../../apps/*/helpers ../../apps/*/src ../../packages/*/src --ignore-pattern='**/node_modules/**' --ignore-pattern='**/dist/**' --ignore-pattern='**/build/**' --ignore-pattern='**/*.d.ts'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hypermod/utils": "^0.7.1",
|
||||
"@types/jscodeshift": "^17.3.0",
|
||||
"ast-types": "0.14.2",
|
||||
"jscodeshift": "^17.3.0",
|
||||
"vitest": "^4.0.8"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import { API, FileInfo, Options } from "jscodeshift";
|
||||
import type { CommentKind, DirectiveKind } from "ast-types/gen/kinds";
|
||||
|
||||
export default function transform(file: FileInfo, api: API, options: Options) {
|
||||
const j = api.jscodeshift;
|
||||
const root = j(file.source);
|
||||
|
||||
const directivesToRemove = new Set(["use client", "use server", "use-client", "use-server"]);
|
||||
|
||||
root.find(j.Program).forEach((path) => {
|
||||
// Handle ASTs where directives are stored in 'directives' property (Babel/TS)
|
||||
if (path.node.directives) {
|
||||
const newDirectives: DirectiveKind[] = [];
|
||||
let capturedComments: CommentKind[] = [];
|
||||
|
||||
path.node.directives.forEach((directive) => {
|
||||
if (directive.type === 'Directive' && directive.value.type === 'DirectiveLiteral' && typeof directive.value.value === 'string' && directivesToRemove.has(directive.value.value)) {
|
||||
// Directive is being removed. Capture its comments.
|
||||
if (directive.comments) {
|
||||
capturedComments.push(...directive.comments);
|
||||
}
|
||||
} else {
|
||||
// Keep this directive
|
||||
// If we have captured comments from previous removed directives, attach them here?
|
||||
// Usually comments belong to the next node.
|
||||
if (capturedComments.length > 0) {
|
||||
directive.comments = directive.comments || [];
|
||||
directive.comments.unshift(...capturedComments);
|
||||
capturedComments = [];
|
||||
}
|
||||
newDirectives.push(directive);
|
||||
}
|
||||
});
|
||||
|
||||
path.node.directives = newDirectives;
|
||||
|
||||
// If we still have captured comments (e.g. all directives removed, or last one removed),
|
||||
// attach them to the first body node.
|
||||
if (capturedComments.length > 0) {
|
||||
if (path.node.body.length > 0) {
|
||||
const firstBodyNode = path.node.body[0];
|
||||
if (firstBodyNode) {
|
||||
firstBodyNode.comments = firstBodyNode.comments || [];
|
||||
firstBodyNode.comments.unshift(...capturedComments);
|
||||
}
|
||||
} else {
|
||||
// If empty body, attach to Program?
|
||||
// jscodeshift might not print Program comments easily if they are not attached to children.
|
||||
// But let's try attaching to the Program node itself if possible, or leave them (they might be lost).
|
||||
path.node.comments = path.node.comments || [];
|
||||
path.node.comments.push(...capturedComments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also handle the case where they might be in body as ExpressionStatements
|
||||
if (path.node.body) {
|
||||
const newBody: any[] = [];
|
||||
let capturedComments: any[] = [];
|
||||
|
||||
path.node.body.forEach((node) => {
|
||||
let shouldRemove = false;
|
||||
if (j.ExpressionStatement.check(node)) {
|
||||
const expression = node.expression;
|
||||
if (j.StringLiteral.check(expression) && directivesToRemove.has(expression.value)) {
|
||||
shouldRemove = true;
|
||||
} else if (j.Literal.check(expression) && typeof expression.value === 'string' && directivesToRemove.has(expression.value)) {
|
||||
shouldRemove = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldRemove) {
|
||||
if (node.comments) {
|
||||
capturedComments.push(...node.comments);
|
||||
}
|
||||
} else {
|
||||
if (capturedComments.length > 0) {
|
||||
node.comments = node.comments || [];
|
||||
node.comments.unshift(...capturedComments);
|
||||
capturedComments = [];
|
||||
}
|
||||
newBody.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
// If comments left at the end (e.g. removed last statement), usually they are trailing comments of the file.
|
||||
// But if we removed the ONLY statement, they become dangling.
|
||||
// We attached to next node, so if we are at the end, there is no next node.
|
||||
// If we removed a directive at the top, and there are more statements, they got attached.
|
||||
|
||||
path.node.body = newBody;
|
||||
}
|
||||
});
|
||||
|
||||
return root.toSource(options);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,572 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { applyTransform } from "@hypermod/utils";
|
||||
import * as transformer from "../function-declaration";
|
||||
|
||||
describe("function-declaration", () => {
|
||||
it("should convert arrow function components to function declarations", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
import React from "react";
|
||||
|
||||
export const MyComponent: React.FC<{}> = () => {
|
||||
return <div>Hello, world!</div>;
|
||||
};
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import React from "react";
|
||||
|
||||
export function MyComponent() {
|
||||
return <div>Hello, world!</div>;
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should handle components with props", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
import React from "react";
|
||||
|
||||
interface IMyComponentProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const MyComponent: React.FC<IMyComponentProps> = ({ name }) => {
|
||||
return <div>Hello, {name}!</div>;
|
||||
};
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import React from "react";
|
||||
|
||||
interface IMyComponentProps {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export function MyComponent(
|
||||
{
|
||||
name
|
||||
}: IMyComponentProps
|
||||
) {
|
||||
return <div>Hello, {name}!</div>;
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should preserve default props", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
import React from "react";
|
||||
|
||||
interface IMyComponentProps {
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export const MyComponent: React.FC<IMyComponentProps> = ({ name = "world" }) => {
|
||||
return <div>Hello, {name}!</div>;
|
||||
};
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import React from "react";
|
||||
|
||||
interface IMyComponentProps {
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export function MyComponent(
|
||||
{
|
||||
name = "world"
|
||||
}: IMyComponentProps
|
||||
) {
|
||||
return <div>Hello, {name}!</div>;
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should not transform non-component arrow functions", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
const myFunction = () => {
|
||||
return "hello";
|
||||
};
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const myFunction = () => {
|
||||
return "hello";
|
||||
};"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should handle observer-wrapped components", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
export const WorkspaceAnalyticsHeader = observer(() => {
|
||||
return <div>Analytics</div>;
|
||||
});
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import { observer } from "mobx-react";
|
||||
|
||||
export const WorkspaceAnalyticsHeader = observer(function WorkspaceAnalyticsHeader() {
|
||||
return <div>Analytics</div>;
|
||||
});"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should handle inline arrow function components", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
export const StarUsOnGitHubLink = () => {
|
||||
return <a href="https://github.com">Star us</a>;
|
||||
};
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"export function StarUsOnGitHubLink() {
|
||||
return <a href="https://github.com">Star us</a>;
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should handle React.FC type without generics", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
import type { FC } from "react";
|
||||
|
||||
export const ProjectAppSidebar: FC = observer(() => {
|
||||
return <div>Sidebar</div>;
|
||||
});
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import type { FC } from "react";
|
||||
|
||||
export const ProjectAppSidebar = observer(function ProjectAppSidebar() {
|
||||
return <div>Sidebar</div>;
|
||||
});"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should handle inline JSX arrow function", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
export const DateAlert = (props: TDateAlertProps) => <></>;
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"export function DateAlert(props: TDateAlertProps) {
|
||||
return <></>;
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should handle observer with generic type parameters", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
export const InstanceProvider = observer<React.FC<React.PropsWithChildren>>((props) => {
|
||||
const { children } = props;
|
||||
return <>{children}</>;
|
||||
});
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import { observer } from "mobx-react";
|
||||
|
||||
export const InstanceProvider = observer(function InstanceProvider(props: React.PropsWithChildren) {
|
||||
const { children } = props;
|
||||
return <>{children}</>;
|
||||
});"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should not add double semicolons after use client directive", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
"use client";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
export const MyComponent = observer(() => {
|
||||
return <div>Hello</div>;
|
||||
});
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
""use client";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
export const MyComponent = observer(function MyComponent() {
|
||||
return <div>Hello</div>;
|
||||
});"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should preserve generic type parameters in wrapper functions", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
import React from "react";
|
||||
|
||||
export const ScatterChart = React.memo(<K extends string, T extends string>(props: TScatterChartProps<K, T>) => {
|
||||
return <div>Chart</div>;
|
||||
});
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import React from "react";
|
||||
|
||||
export const ScatterChart = React.memo(
|
||||
function ScatterChart<K extends string, T extends string>(props: TScatterChartProps<K, T>) {
|
||||
return <div>Chart</div>;
|
||||
}
|
||||
);"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should preserve generic type parameters on React.forwardRef", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
import React from "react";
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
|
||||
return <button ref={ref}>Click me</button>;
|
||||
});
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import React from "react";
|
||||
|
||||
const Button = React.forwardRef(
|
||||
function Button(props: ButtonProps, ref: React.ForwardedRef<HTMLButtonElement>) {
|
||||
return <button ref={ref}>Click me</button>;
|
||||
}
|
||||
);"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should prefix unused props parameter with underscore", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
import type { TCallbackMentionComponentProps } from "@plane/editor";
|
||||
|
||||
export const EditorAdditionalMentionsRoot: React.FC<TCallbackMentionComponentProps> = () => null;
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import type { TCallbackMentionComponentProps } from "@plane/editor";
|
||||
|
||||
export function EditorAdditionalMentionsRoot(_props: TCallbackMentionComponentProps) {
|
||||
return null;
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should add Record<string, unknown> type for React.forwardRef with only element type", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
import { forwardRef } from "react";
|
||||
|
||||
const ListLoaderItemRow = forwardRef<HTMLDivElement>((props, ref) => (
|
||||
<div ref={ref}>Content</div>
|
||||
));
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import { forwardRef } from "react";
|
||||
|
||||
const ListLoaderItemRow = forwardRef(
|
||||
function ListLoaderItemRow(props: Record<string, unknown>, ref: React.ForwardedRef<HTMLDivElement>) {
|
||||
return (<div ref={ref}>Content</div>);
|
||||
}
|
||||
);"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should preserve comments in function body", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
export const PreloadResources = () => (
|
||||
// usePreloadResources();
|
||||
null
|
||||
);
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"export function PreloadResources() {
|
||||
return (
|
||||
// usePreloadResources();
|
||||
(null)
|
||||
);
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should preserve leading comments before export declaration", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
"use client";
|
||||
|
||||
// TODO: Check if we need this
|
||||
// https://nextjs.org/docs/app/api-reference/functions/generate-metadata#link-relpreload
|
||||
// export const usePreloadResources = () => {
|
||||
// useEffect(() => {
|
||||
// const preloadItem = (url: string) => {
|
||||
// ReactDOM.preload(url, { as: "fetch", crossOrigin: "use-credentials" });
|
||||
// };
|
||||
//
|
||||
// const urls = [
|
||||
// \`\${process.env.VITE_API_BASE_URL}/api/instances/\`,
|
||||
// \`\${process.env.VITE_API_BASE_URL}/api/users/me/\`,
|
||||
// \`\${process.env.VITE_API_BASE_URL}/api/users/me/profile/\`,
|
||||
// \`\${process.env.VITE_API_BASE_URL}/api/users/me/settings/\`,
|
||||
// \`\${process.env.VITE_API_BASE_URL}/api/users/me/workspaces/?v=\${Date.now()}\`,
|
||||
// ];
|
||||
//
|
||||
// urls.forEach((url) => preloadItem(url));
|
||||
// }, []);
|
||||
// };
|
||||
|
||||
export const PreloadResources = () =>
|
||||
// usePreloadResources();
|
||||
null;
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
""use client";
|
||||
|
||||
// TODO: Check if we need this
|
||||
// https://nextjs.org/docs/app/api-reference/functions/generate-metadata#link-relpreload
|
||||
// export const usePreloadResources = () => {
|
||||
// useEffect(() => {
|
||||
// const preloadItem = (url: string) => {
|
||||
// ReactDOM.preload(url, { as: "fetch", crossOrigin: "use-credentials" });
|
||||
// };
|
||||
//
|
||||
// const urls = [
|
||||
// \`\${process.env.VITE_API_BASE_URL}/api/instances/\`,
|
||||
// \`\${process.env.VITE_API_BASE_URL}/api/users/me/\`,
|
||||
// \`\${process.env.VITE_API_BASE_URL}/api/users/me/profile/\`,
|
||||
// \`\${process.env.VITE_API_BASE_URL}/api/users/me/settings/\`,
|
||||
// \`\${process.env.VITE_API_BASE_URL}/api/users/me/workspaces/?v=\${Date.now()}\`,
|
||||
// ];
|
||||
//
|
||||
// urls.forEach((url) => preloadItem(url));
|
||||
// }, []);
|
||||
// };
|
||||
|
||||
export function PreloadResources() {
|
||||
return (
|
||||
// usePreloadResources();
|
||||
null
|
||||
);
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should preserve leading comments before wrapped export declaration", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
// This is a wrapped component
|
||||
// It uses observer for reactivity
|
||||
export const MyObserverComponent = observer(() => {
|
||||
return <div>Observer component</div>;
|
||||
});
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"// This is a wrapped component
|
||||
// It uses observer for reactivity
|
||||
export const MyObserverComponent = observer(function MyObserverComponent() {
|
||||
return <div>Observer component</div>;
|
||||
});"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should preserve trailing comments on exported variable declaration", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
export const Foo = () => <div />; // trailing comment
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toContain("// trailing comment");
|
||||
});
|
||||
|
||||
it("should preserve leading comments on exported variable declaration inside export", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
export /* leading comment */ const Foo = () => <div />;
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toContain("/* leading comment */");
|
||||
});
|
||||
|
||||
it("should preserve dependency arrays when transforming wrapped components", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
import { useMemo } from "react";
|
||||
|
||||
const MyComponent = useMemo(() => {
|
||||
return () => <div>Hello</div>;
|
||||
}, [dep]);
|
||||
`,
|
||||
{ parser: "tsx", path: "file.tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import { useMemo } from "react";
|
||||
|
||||
const MyComponent = useMemo(function MyComponent() {
|
||||
return () => <div>Hello</div>;
|
||||
}, [dep]);"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should preserve dependency arrays for constants that look like components", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
import { useMemo } from "react";
|
||||
|
||||
const ACTION_HANDLERS = useMemo(function ACTION_HANDLERS() {
|
||||
return {
|
||||
archived: () => {},
|
||||
};
|
||||
}, []);
|
||||
`,
|
||||
{ parser: "tsx", path: "file.tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import { useMemo } from "react";
|
||||
|
||||
const ACTION_HANDLERS = useMemo(function ACTION_HANDLERS() {
|
||||
return {
|
||||
archived: () => {},
|
||||
};
|
||||
}, []);"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should handle memo with generic type arguments correctly", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
import { memo } from "react";
|
||||
|
||||
type TAccessMenuProps = {
|
||||
currentAccess: number;
|
||||
};
|
||||
|
||||
export const AccessMenu = memo<TAccessMenuProps>(
|
||||
({ currentAccess }) => {
|
||||
return <div>{currentAccess}</div>;
|
||||
}
|
||||
);
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import { memo } from "react";
|
||||
|
||||
type TAccessMenuProps = {
|
||||
currentAccess: number;
|
||||
};
|
||||
|
||||
export const AccessMenu = memo(function AccessMenu(
|
||||
{
|
||||
currentAccess
|
||||
}: TAccessMenuProps
|
||||
) {
|
||||
return <div>{currentAccess}</div>;
|
||||
});"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should not transform CONSTANT_CASE variables", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
export const MY_CONSTANT = () => {
|
||||
return "value";
|
||||
};
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"export const MY_CONSTANT = () => {
|
||||
return "value";
|
||||
};"
|
||||
`);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,296 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { applyTransform } from "@hypermod/utils";
|
||||
import * as transformer from "../remove-directives";
|
||||
|
||||
describe("remove-directives", () => {
|
||||
it("should remove 'use client' directive", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
"use client";
|
||||
import React from "react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <div>Hello, world!</div>;
|
||||
};
|
||||
`,
|
||||
{ parser: "tsx" },
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import React from "react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <div>Hello, world!</div>;
|
||||
};"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should remove 'use server' directive", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
"use server";
|
||||
import db from "./db";
|
||||
|
||||
export const getData = async () => {
|
||||
return db.query("SELECT * FROM users");
|
||||
};
|
||||
`,
|
||||
{ parser: "ts" },
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import db from "./db";
|
||||
|
||||
export const getData = async () => {
|
||||
return db.query("SELECT * FROM users");
|
||||
};"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should remove 'use client' directive with single quotes", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
'use client';
|
||||
import React from "react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <div>Hello, world!</div>;
|
||||
};
|
||||
`,
|
||||
{ parser: "tsx" },
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import React from "react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <div>Hello, world!</div>;
|
||||
};"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should remove multiple directives", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
"use client";
|
||||
"use strict";
|
||||
import React from "react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <div>Hello, world!</div>;
|
||||
};
|
||||
`,
|
||||
{ parser: "tsx" },
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
""use strict";;
|
||||
import React from "react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
return <div>Hello, world!</div>;
|
||||
};"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should ignore directives inside functions", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
import React from "react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
"use client";
|
||||
return <div>Hello, world!</div>;
|
||||
};
|
||||
`,
|
||||
{ parser: "tsx" },
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import React from "react";
|
||||
|
||||
export const MyComponent = () => {
|
||||
"use client";
|
||||
return <div>Hello, world!</div>;
|
||||
};"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should preserve comments", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
// comment before
|
||||
"use client";
|
||||
// comment after
|
||||
import React from "react";
|
||||
`,
|
||||
{ parser: "tsx" }
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"// comment before
|
||||
// comment after
|
||||
import React from "react";"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should remove 'use-client' directive with hyphen", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
"use-client";
|
||||
import type { FC } from "react";
|
||||
// types
|
||||
import type { TDeDupeIssue } from "@plane/types";
|
||||
|
||||
type TDuplicateModalRootProps = {
|
||||
workspaceSlug: string;
|
||||
issues: TDeDupeIssue[];
|
||||
handleDuplicateIssueModal: (value: boolean) => void;
|
||||
};
|
||||
|
||||
export function DuplicateModalRoot(props: TDuplicateModalRootProps) {
|
||||
const { workspaceSlug, issues, handleDuplicateIssueModal } = props;
|
||||
return <></>;
|
||||
}
|
||||
`,
|
||||
{ parser: "tsx" },
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import type { FC } from "react";
|
||||
// types
|
||||
import type { TDeDupeIssue } from "@plane/types";
|
||||
|
||||
type TDuplicateModalRootProps = {
|
||||
workspaceSlug: string;
|
||||
issues: TDeDupeIssue[];
|
||||
handleDuplicateIssueModal: (value: boolean) => void;
|
||||
};
|
||||
|
||||
export function DuplicateModalRoot(props: TDuplicateModalRootProps) {
|
||||
const { workspaceSlug, issues, handleDuplicateIssueModal } = props;
|
||||
return <></>;
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should remove 'use-client' directive with hyphen and single quotes", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
'use-client';
|
||||
import type { FC } from "react";
|
||||
import { useState } from "react";
|
||||
// plane imports
|
||||
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
||||
|
||||
export function MyComponent() {
|
||||
return <div>Hello</div>;
|
||||
}
|
||||
`,
|
||||
{ parser: "tsx" },
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import type { FC } from "react";
|
||||
import { useState } from "react";
|
||||
// plane imports
|
||||
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
||||
|
||||
export function MyComponent() {
|
||||
return <div>Hello</div>;
|
||||
}"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should remove 'use-server' directive with hyphen", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
"use-server";
|
||||
import db from "./db";
|
||||
|
||||
export const getData = async () => {
|
||||
return db.query("SELECT * FROM users");
|
||||
};
|
||||
`,
|
||||
{ parser: "ts" },
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import db from "./db";
|
||||
|
||||
export const getData = async () => {
|
||||
return db.query("SELECT * FROM users");
|
||||
};"
|
||||
`);
|
||||
});
|
||||
|
||||
it("should handle 'use-client' in modal component structure", async () => {
|
||||
const result = await applyTransform(
|
||||
transformer,
|
||||
`
|
||||
"use-client";
|
||||
|
||||
import type { FC } from "react";
|
||||
import { useState } from "react";
|
||||
// plane imports
|
||||
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
||||
// hooks
|
||||
import useKeypress from "@/hooks/use-keypress";
|
||||
// local imports
|
||||
import { InboxIssueCreateRoot } from "./create-root";
|
||||
|
||||
type TInboxIssueCreateModalRoot = {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
modalState: boolean;
|
||||
handleModalClose: () => void;
|
||||
};
|
||||
|
||||
export function InboxIssueCreateModalRoot(props: TInboxIssueCreateModalRoot) {
|
||||
const { workspaceSlug, projectId, modalState, handleModalClose } = props;
|
||||
return <></>;
|
||||
}
|
||||
`,
|
||||
{ parser: "tsx" },
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"import type { FC } from "react";
|
||||
import { useState } from "react";
|
||||
// plane imports
|
||||
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
||||
// hooks
|
||||
import useKeypress from "@/hooks/use-keypress";
|
||||
// local imports
|
||||
import { InboxIssueCreateRoot } from "./create-root";
|
||||
|
||||
type TInboxIssueCreateModalRoot = {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
modalState: boolean;
|
||||
handleModalClose: () => void;
|
||||
};
|
||||
|
||||
export function InboxIssueCreateModalRoot(props: TInboxIssueCreateModalRoot) {
|
||||
const { workspaceSlug, projectId, modalState, handleModalClose } = props;
|
||||
return <></>;
|
||||
}"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from "vitest/config";
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
environment: "node",
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
.next/
|
||||
.react-router/
|
||||
.turbo/
|
||||
.vite/
|
||||
build/
|
||||
dist/
|
||||
node_modules/
|
||||
out/
|
||||
pnpm-lock.yaml
|
||||
storybook-static/
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "@plane/constants",
|
||||
"version": "1.3.0",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.mjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.mts",
|
||||
"exports": {
|
||||
".": "./dist/index.mjs",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "tsdown --watch --no-clean",
|
||||
"build": "tsdown",
|
||||
"check:lint": "oxlint --max-warnings=2 .",
|
||||
"check:types": "tsc --noEmit",
|
||||
"check:format": "oxfmt --check .",
|
||||
"fix:lint": "oxlint --fix .",
|
||||
"fix:format": "oxfmt .",
|
||||
"clean": "rm -rf .turbo && rm -rf .next && rm -rf node_modules && rm -rf dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@plane/types": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@plane/typescript-config": "workspace:*",
|
||||
"@types/node": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export enum AI_EDITOR_TASKS {
|
||||
ASK_ANYTHING = "ASK_ANYTHING",
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { TAnalyticsTabsBase } from "@plane/types";
|
||||
import { ChartXAxisProperty, ChartYAxisMetric } 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"];
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export * from "./common";
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { TCoreLoginMediums } from "@plane/types";
|
||||
|
||||
export const CORE_LOGIN_MEDIUM_LABELS: Record<TCoreLoginMediums, string> = {
|
||||
email: "Email",
|
||||
"magic-code": "Magic code",
|
||||
github: "GitHub",
|
||||
gitlab: "GitLab",
|
||||
google: "Google",
|
||||
gitea: "Gitea",
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { TExtendedLoginMediums } from "@plane/types";
|
||||
|
||||
export const EXTENDED_LOGIN_MEDIUM_LABELS: Record<TExtendedLoginMediums, string> = {};
|
||||
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { TLoginMediums } from "@plane/types";
|
||||
import { CORE_LOGIN_MEDIUM_LABELS } from "./core";
|
||||
import { EXTENDED_LOGIN_MEDIUM_LABELS } from "./extended";
|
||||
|
||||
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",
|
||||
PASSWORD_TOO_WEAK = "5021",
|
||||
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",
|
||||
}
|
||||
|
||||
export const LOGIN_MEDIUM_LABELS: Record<TLoginMediums, string> = {
|
||||
...CORE_LOGIN_MEDIUM_LABELS,
|
||||
...EXTENDED_LOGIN_MEDIUM_LABELS,
|
||||
} as const;
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { TChartColorScheme } from "@plane/types";
|
||||
import { ChartXAxisProperty } from "@plane/types";
|
||||
|
||||
export const LABEL_CLASSNAME = "uppercase text-tertiary/60 text-13 tracking-wide";
|
||||
export const AXIS_LABEL_CLASSNAME = "uppercase text-tertiary/60 text-13 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",
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
// 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-success-primary",
|
||||
bgColor: "bg-success-subtle",
|
||||
},
|
||||
{
|
||||
i18n_label: "project_cycles.status.draft",
|
||||
value: "draft",
|
||||
i18n_title: "project_cycles.status.draft",
|
||||
color: "#525252",
|
||||
textColor: "text-tertiary",
|
||||
bgColor: "bg-surface-2",
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
// types
|
||||
import type { 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-success-subtle",
|
||||
"bg-danger-subtle",
|
||||
"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;
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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",
|
||||
];
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export const API_BASE_URL = process.env.VITE_API_BASE_URL || "";
|
||||
export const API_BASE_PATH = process.env.VITE_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.VITE_ADMIN_BASE_URL || "";
|
||||
export const ADMIN_BASE_PATH = process.env.VITE_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.VITE_SPACE_BASE_URL || "";
|
||||
export const SPACE_BASE_PATH = process.env.VITE_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.VITE_LIVE_BASE_URL || "";
|
||||
export const LIVE_BASE_PATH = process.env.VITE_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.VITE_WEB_BASE_URL || "";
|
||||
export const WEB_BASE_PATH = process.env.VITE_WEB_BASE_PATH || "";
|
||||
export const WEB_URL = encodeURI(`${WEB_BASE_URL}${WEB_BASE_PATH}`);
|
||||
// plane website url
|
||||
export const WEBSITE_URL = process.env.VITE_WEBSITE_URL || "https://plane.so";
|
||||
// support email
|
||||
export const SUPPORT_EMAIL = process.env.VITE_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";
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
// plane imports
|
||||
import type { 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,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,508 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { 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",
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export * from "./core";
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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": [],
|
||||
};
|
||||
|
||||
/**
|
||||
* Dangerous file extensions that should be blocked
|
||||
*/
|
||||
export const DANGEROUS_EXTENSIONS = [
|
||||
"exe",
|
||||
"bat",
|
||||
"cmd",
|
||||
"sh",
|
||||
"php",
|
||||
"asp",
|
||||
"aspx",
|
||||
"jsp",
|
||||
"cgi",
|
||||
"dll",
|
||||
"vbs",
|
||||
"jar",
|
||||
"ps1",
|
||||
];
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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",
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export const CHARTS_THEME = {
|
||||
background: "transparent",
|
||||
text: {
|
||||
color: "var(--text-color-secondary)",
|
||||
},
|
||||
axis: {
|
||||
domain: {
|
||||
line: {
|
||||
stroke: "var(--background-color-layer-2)",
|
||||
strokeWidth: 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
container: {
|
||||
background: "var(--background-color-layer-2)",
|
||||
color: "var(--text-color-secondary)",
|
||||
fontSize: "0.8rem",
|
||||
border: "1px solid var(--border-color-strong)",
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
line: {
|
||||
stroke: "var(--border-color-subtle)",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const CHART_DEFAULT_MARGIN = {
|
||||
top: 50,
|
||||
right: 50,
|
||||
bottom: 50,
|
||||
left: 50,
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export enum EIconSize {
|
||||
XS = "xs",
|
||||
SM = "sm",
|
||||
MD = "md",
|
||||
LG = "lg",
|
||||
XL = "xl",
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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";
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export enum EInstanceStatus {
|
||||
ERROR = "ERROR",
|
||||
NOT_YET_READY = "NOT_YET_READY",
|
||||
}
|
||||
|
||||
export type TInstanceStatus = {
|
||||
status: EInstanceStatus | undefined;
|
||||
data?: object;
|
||||
};
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { TInboxIssueStatus } from "@plane/types";
|
||||
import { EInboxIssueStatus } 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,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,366 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type {
|
||||
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: "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;
|
||||
@@ -0,0 +1,368 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type {
|
||||
IIssueFilterOptions,
|
||||
ILayoutDisplayFiltersOptions,
|
||||
TIssueActivityComment,
|
||||
TWorkItemFilterProperty,
|
||||
} from "@plane/types";
|
||||
import { EIssuesStoreType } from "@plane/types";
|
||||
import type { TIssueFilterPriorityObject } from "./common";
|
||||
import { ISSUE_DISPLAY_PROPERTIES_KEYS, SUB_ISSUES_DISPLAY_PROPERTIES_KEYS } from "./common";
|
||||
|
||||
import type { 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-layer-2 text-priority-urgent border-strong",
|
||||
icon: "error",
|
||||
},
|
||||
{
|
||||
key: "high",
|
||||
titleTranslationKey: "issue.priority.high",
|
||||
className: "bg-layer-2 text-priority-high border-strong",
|
||||
icon: "signal_cellular_alt",
|
||||
},
|
||||
{
|
||||
key: "medium",
|
||||
titleTranslationKey: "issue.priority.medium",
|
||||
className: "bg-layer-2 text-priority-medium border-strong",
|
||||
icon: "signal_cellular_alt_2_bar",
|
||||
},
|
||||
{
|
||||
key: "low",
|
||||
titleTranslationKey: "issue.priority.low",
|
||||
className: "bg-layer-2 text-priority-low border-strong",
|
||||
icon: "signal_cellular_alt_1_bar",
|
||||
},
|
||||
{
|
||||
key: "none",
|
||||
titleTranslationKey: "common.none",
|
||||
className: "bg-layer-2 text-priority-none border-strong",
|
||||
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;
|
||||
|
||||
export const BASE_ACTIVITY_FILTER_TYPES = [
|
||||
EActivityFilterType.ACTIVITY,
|
||||
EActivityFilterType.STATE,
|
||||
EActivityFilterType.ASSIGNEE,
|
||||
EActivityFilterType.DEFAULT,
|
||||
];
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export * from "./common";
|
||||
export * from "./filter";
|
||||
export * from "./layout";
|
||||
export * from "./modal";
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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);
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
// plane imports
|
||||
import type { 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,
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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];
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
// 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,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export const SITE_NAME = "NODE.DC | Self-hosted task management workspace.";
|
||||
export const SITE_TITLE = "NODE.DC | Self-hosted task management workspace.";
|
||||
export const SITE_DESCRIPTION =
|
||||
"Self-hosted task management workspace for projects, work items, and internal operational flows.";
|
||||
export const SITE_KEYWORDS =
|
||||
"nodedc, task management, workflow, project management, self-hosted, work items, internal operations, kanban, collaboration";
|
||||
export const SITE_URL = "https://app.plane.so/";
|
||||
export const TWITTER_USER_NAME = "NODE.DC";
|
||||
|
||||
// Plane Sites Metadata
|
||||
export const SPACE_SITE_NAME = "NODE.DC Publish | Publish boards and roadmaps with one click.";
|
||||
export const SPACE_SITE_TITLE = "NODE.DC Publish | Publish boards and roadmaps with one click";
|
||||
export const SPACE_SITE_DESCRIPTION = "NODE.DC Publish exposes boards and roadmaps from your self-hosted workspace.";
|
||||
export const SPACE_SITE_KEYWORDS =
|
||||
"nodedc, publish, roadmap, board, self-hosted, project management, work items";
|
||||
export const SPACE_SITE_URL = "https://app.plane.so/";
|
||||
export const SPACE_TWITTER_USER_NAME = "NODE.DC";
|
||||
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
// types
|
||||
import type { 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-placeholder",
|
||||
bgColor: "bg-layer-1",
|
||||
},
|
||||
{
|
||||
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-tertiary",
|
||||
bgColor: "bg-surface-2",
|
||||
},
|
||||
{
|
||||
i18n_label: "project_modules.status.completed",
|
||||
value: "completed",
|
||||
color: MODULE_STATUS_COLORS.completed,
|
||||
textColor: "text-success-primary",
|
||||
bgColor: "bg-success-subtle",
|
||||
},
|
||||
{
|
||||
i18n_label: "project_modules.status.cancelled",
|
||||
value: "cancelled",
|
||||
color: MODULE_STATUS_COLORS.cancelled,
|
||||
textColor: "text-danger-primary",
|
||||
bgColor: "bg-danger-subtle",
|
||||
},
|
||||
];
|
||||
|
||||
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",
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { 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" },
|
||||
];
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { IPaymentProduct, TBillingFrequency, TProductBillingFrequency } from "@plane/types";
|
||||
import { EProductSubscriptionEnum } 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",
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
// plane imports
|
||||
import { EStartOfTheWeek } from "@plane/types";
|
||||
|
||||
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",
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* @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",
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
// plane imports
|
||||
import type { TProjectAppliedDisplayFilterKeys, TProjectOrderByOptions } from "@plane/types";
|
||||
// local imports
|
||||
|
||||
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_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 enum EProjectFeatureKey {
|
||||
WORK_ITEMS = "work_items",
|
||||
CYCLES = "cycles",
|
||||
MODULES = "modules",
|
||||
VIEWS = "views",
|
||||
PAGES = "pages",
|
||||
INTAKE = "intake",
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export * from "./operator-labels";
|
||||
export * from "./option";
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { TCoreSupportedOperators, TCoreSupportedDateFilterOperators } from "@plane/types";
|
||||
import { CORE_EQUALITY_OPERATOR, CORE_COLLECTION_OPERATOR, CORE_COMPARISON_OPERATOR } 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;
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { 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;
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { 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";
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { 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;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export * from "./profile";
|
||||
export * from "./project";
|
||||
export * from "./workspace";
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
// plane imports
|
||||
import type { TProfileSettingsTabs } from "@plane/types";
|
||||
|
||||
export enum PROFILE_SETTINGS_CATEGORY {
|
||||
YOUR_PROFILE = "your profile",
|
||||
DEVELOPER = "developer",
|
||||
}
|
||||
|
||||
export const PROFILE_SETTINGS_CATEGORIES: PROFILE_SETTINGS_CATEGORY[] = [
|
||||
PROFILE_SETTINGS_CATEGORY.YOUR_PROFILE,
|
||||
PROFILE_SETTINGS_CATEGORY.DEVELOPER,
|
||||
];
|
||||
|
||||
export const PROFILE_SETTINGS: Record<
|
||||
TProfileSettingsTabs,
|
||||
{
|
||||
key: TProfileSettingsTabs;
|
||||
i18n_label: string;
|
||||
}
|
||||
> = {
|
||||
general: {
|
||||
key: "general",
|
||||
i18n_label: "profile.actions.profile",
|
||||
},
|
||||
security: {
|
||||
key: "security",
|
||||
i18n_label: "profile.actions.security",
|
||||
},
|
||||
activity: {
|
||||
key: "activity",
|
||||
i18n_label: "profile.actions.activity",
|
||||
},
|
||||
preferences: {
|
||||
key: "preferences",
|
||||
i18n_label: "profile.actions.preferences",
|
||||
},
|
||||
notifications: {
|
||||
key: "notifications",
|
||||
i18n_label: "profile.actions.notifications",
|
||||
},
|
||||
"api-tokens": {
|
||||
key: "api-tokens",
|
||||
i18n_label: "profile.actions.api-tokens",
|
||||
},
|
||||
};
|
||||
|
||||
export const PROFILE_SETTINGS_TABS: TProfileSettingsTabs[] = Object.keys(PROFILE_SETTINGS) as TProfileSettingsTabs[];
|
||||
|
||||
export const GROUPED_PROFILE_SETTINGS: Record<
|
||||
PROFILE_SETTINGS_CATEGORY,
|
||||
{ key: TProfileSettingsTabs; i18n_label: string }[]
|
||||
> = {
|
||||
[PROFILE_SETTINGS_CATEGORY.YOUR_PROFILE]: [
|
||||
PROFILE_SETTINGS["general"],
|
||||
PROFILE_SETTINGS["preferences"],
|
||||
PROFILE_SETTINGS["notifications"],
|
||||
PROFILE_SETTINGS["security"],
|
||||
PROFILE_SETTINGS["activity"],
|
||||
],
|
||||
[PROFILE_SETTINGS_CATEGORY.DEVELOPER]: [PROFILE_SETTINGS["api-tokens"]],
|
||||
};
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
// plane imports
|
||||
import { EUserProjectRoles } from "@plane/types";
|
||||
import type { TProjectSettingsItem, TProjectSettingsTabs } from "@plane/types";
|
||||
|
||||
export enum PROJECT_SETTINGS_CATEGORY {
|
||||
GENERAL = "general",
|
||||
FEATURES = "features",
|
||||
WORK_STRUCTURE = "work-structure",
|
||||
EXECUTION = "execution",
|
||||
}
|
||||
|
||||
export const PROJECT_SETTINGS_CATEGORIES: PROJECT_SETTINGS_CATEGORY[] = [
|
||||
PROJECT_SETTINGS_CATEGORY.GENERAL,
|
||||
PROJECT_SETTINGS_CATEGORY.FEATURES,
|
||||
PROJECT_SETTINGS_CATEGORY.WORK_STRUCTURE,
|
||||
PROJECT_SETTINGS_CATEGORY.EXECUTION,
|
||||
];
|
||||
|
||||
export const PROJECT_SETTINGS: Record<TProjectSettingsTabs, TProjectSettingsItem> = {
|
||||
general: {
|
||||
key: "general",
|
||||
i18n_label: "common.general",
|
||||
href: ``,
|
||||
access: [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER, EUserProjectRoles.GUEST],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/`,
|
||||
},
|
||||
members: {
|
||||
key: "members",
|
||||
i18n_label: "common.members",
|
||||
href: `/members`,
|
||||
access: [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER, EUserProjectRoles.GUEST],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/members/`,
|
||||
},
|
||||
features_cycles: {
|
||||
key: "features_cycles",
|
||||
i18n_label: "project_settings.features.cycles.short_title",
|
||||
href: `/features/cycles`,
|
||||
access: [EUserProjectRoles.ADMIN],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/features/cycles/`,
|
||||
},
|
||||
features_modules: {
|
||||
key: "features_modules",
|
||||
i18n_label: "project_settings.features.modules.short_title",
|
||||
href: `/features/modules`,
|
||||
access: [EUserProjectRoles.ADMIN],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/features/modules/`,
|
||||
},
|
||||
features_views: {
|
||||
key: "features_views",
|
||||
i18n_label: "project_settings.features.views.short_title",
|
||||
href: `/features/views`,
|
||||
access: [EUserProjectRoles.ADMIN],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/features/views/`,
|
||||
},
|
||||
features_pages: {
|
||||
key: "features_pages",
|
||||
i18n_label: "project_settings.features.pages.short_title",
|
||||
href: `/features/pages`,
|
||||
access: [EUserProjectRoles.ADMIN],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/features/pages/`,
|
||||
},
|
||||
features_intake: {
|
||||
key: "features_intake",
|
||||
i18n_label: "project_settings.features.intake.short_title",
|
||||
href: `/features/intake`,
|
||||
access: [EUserProjectRoles.ADMIN],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/features/intake/`,
|
||||
},
|
||||
states: {
|
||||
key: "states",
|
||||
i18n_label: "common.states",
|
||||
href: `/states`,
|
||||
access: [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/states/`,
|
||||
},
|
||||
labels: {
|
||||
key: "labels",
|
||||
i18n_label: "common.labels",
|
||||
href: `/labels`,
|
||||
access: [EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/labels/`,
|
||||
},
|
||||
estimates: {
|
||||
key: "estimates",
|
||||
i18n_label: "common.estimates",
|
||||
href: `/estimates`,
|
||||
access: [EUserProjectRoles.ADMIN],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/estimates/`,
|
||||
},
|
||||
automations: {
|
||||
key: "automations",
|
||||
i18n_label: "project_settings.automations.label",
|
||||
href: `/automations`,
|
||||
access: [EUserProjectRoles.ADMIN],
|
||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/automations/`,
|
||||
},
|
||||
};
|
||||
|
||||
export const PROJECT_SETTINGS_FLAT_MAP: TProjectSettingsItem[] = Object.values(PROJECT_SETTINGS);
|
||||
|
||||
export const GROUPED_PROJECT_SETTINGS: Record<PROJECT_SETTINGS_CATEGORY, TProjectSettingsItem[]> = {
|
||||
[PROJECT_SETTINGS_CATEGORY.GENERAL]: [PROJECT_SETTINGS["general"], PROJECT_SETTINGS["members"]],
|
||||
[PROJECT_SETTINGS_CATEGORY.FEATURES]: [
|
||||
PROJECT_SETTINGS["features_cycles"],
|
||||
PROJECT_SETTINGS["features_modules"],
|
||||
PROJECT_SETTINGS["features_views"],
|
||||
PROJECT_SETTINGS["features_pages"],
|
||||
PROJECT_SETTINGS["features_intake"],
|
||||
],
|
||||
[PROJECT_SETTINGS_CATEGORY.WORK_STRUCTURE]: [
|
||||
PROJECT_SETTINGS["states"],
|
||||
PROJECT_SETTINGS["labels"],
|
||||
PROJECT_SETTINGS["estimates"],
|
||||
],
|
||||
[PROJECT_SETTINGS_CATEGORY.EXECUTION]: [PROJECT_SETTINGS["automations"]],
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
// plane imports
|
||||
import type { TWorkspaceSettingsItem, TWorkspaceSettingsTabs } from "@plane/types";
|
||||
import { EUserWorkspaceRoles } from "@plane/types";
|
||||
|
||||
export enum WORKSPACE_SETTINGS_CATEGORY {
|
||||
ADMINISTRATION = "administration",
|
||||
FEATURES = "features",
|
||||
DEVELOPER = "developer",
|
||||
}
|
||||
|
||||
export const WORKSPACE_SETTINGS_CATEGORIES: WORKSPACE_SETTINGS_CATEGORY[] = [
|
||||
WORKSPACE_SETTINGS_CATEGORY.ADMINISTRATION,
|
||||
WORKSPACE_SETTINGS_CATEGORY.FEATURES,
|
||||
WORKSPACE_SETTINGS_CATEGORY.DEVELOPER,
|
||||
];
|
||||
|
||||
export const WORKSPACE_SETTINGS: Record<TWorkspaceSettingsTabs, TWorkspaceSettingsItem> = {
|
||||
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 GROUPED_WORKSPACE_SETTINGS: Record<WORKSPACE_SETTINGS_CATEGORY, TWorkspaceSettingsItem[]> = {
|
||||
[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"]],
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export const SIDEBAR_WIDTH = 250;
|
||||
export const EXTENDED_SIDEBAR_WIDTH = 300;
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export const SPREADSHEET_SELECT_GROUP = "spreadsheet-issues";
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { 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;
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export const STICKIES_PER_PAGE = 30;
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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",
|
||||
];
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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",
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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: {},
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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" },
|
||||
];
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export enum EDraftIssuePaginationType {
|
||||
INIT = "INIT",
|
||||
NEXT = "NEXT",
|
||||
PREV = "PREV",
|
||||
CURRENT = "CURRENT",
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { TStaticViewTypes, IWorkspaceSearchResults } from "@plane/types";
|
||||
import { EUserWorkspaceRoles } from "@plane/types";
|
||||
|
||||
export const ORGANIZATION_SIZE: string[] = ["Just myself", "2-10", "11-50", "51-200", "201-500", "500+"];
|
||||
|
||||
export const RESTRICTED_URLS: string[] = [
|
||||
"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 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.includes(url),
|
||||
},
|
||||
analytics: {
|
||||
key: "analytics",
|
||||
labelTranslationKey: "analytics",
|
||||
href: `/analytics/`,
|
||||
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["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),
|
||||
},
|
||||
stickies: {
|
||||
key: "stickies",
|
||||
labelTranslationKey: "sidebar.stickies",
|
||||
href: `/stickies/`,
|
||||
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST],
|
||||
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),
|
||||
},
|
||||
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"],
|
||||
];
|
||||
|
||||
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",
|
||||
];
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "@plane/typescript-config/node-library.json",
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { defineConfig } from "tsdown";
|
||||
|
||||
export default defineConfig({
|
||||
entry: ["src/index.ts"],
|
||||
format: ["esm"],
|
||||
dts: true,
|
||||
exports: true,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
.next/
|
||||
.react-router/
|
||||
.turbo/
|
||||
.vite/
|
||||
build/
|
||||
dist/
|
||||
node_modules/
|
||||
out/
|
||||
pnpm-lock.yaml
|
||||
storybook-static/
|
||||
@@ -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).
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "@plane/decorators",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "Controller and route decorators for Express.js applications",
|
||||
"license": "AGPL-3.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.mjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"types": "./dist/index.d.mts",
|
||||
"exports": {
|
||||
".": "./dist/index.mjs",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsdown",
|
||||
"dev": "tsdown --watch --no-clean",
|
||||
"check:lint": "oxlint --max-warnings=3 .",
|
||||
"check:types": "tsc --noEmit",
|
||||
"check:format": "oxfmt --check .",
|
||||
"fix:lint": "oxlint --fix .",
|
||||
"fix:format": "oxfmt .",
|
||||
"clean": "rm -rf .turbo && rm -rf .next && rm -rf node_modules && rm -rf dist"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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:"
|
||||
},
|
||||
"inlinedDependencies": {
|
||||
"@types/express": "4.17.23",
|
||||
"@types/express-serve-static-core": "4.19.6",
|
||||
"reflect-metadata": "0.2.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export * from "./controller";
|
||||
export * from "./rest";
|
||||
export * from "./websocket";
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import "reflect-metadata";
|
||||
import type { 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);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "@plane/typescript-config/node-library.json",
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["dist", "build", "node_modules"]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { defineConfig } from "tsdown";
|
||||
|
||||
export default defineConfig({
|
||||
entry: ["src/index.ts"],
|
||||
format: ["esm"],
|
||||
dts: true,
|
||||
exports: true,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
.next/
|
||||
.react-router/
|
||||
.turbo/
|
||||
.vite/
|
||||
build/
|
||||
dist/
|
||||
node_modules/
|
||||
out/
|
||||
pnpm-lock.yaml
|
||||
storybook-static/
|
||||
@@ -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 Editor’s 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 Editor’s 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
|
||||
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"name": "@plane/editor",
|
||||
"version": "1.3.0",
|
||||
"private": true,
|
||||
"description": "Core Editor that powers Plane",
|
||||
"keywords": [
|
||||
"editor",
|
||||
"markdown",
|
||||
"nextjs",
|
||||
"react",
|
||||
"rich-text"
|
||||
],
|
||||
"license": "AGPL-3.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./lib": "./dist/lib.js",
|
||||
"./package.json": "./package.json",
|
||||
"./styles.css": "./dist/styles/index.css",
|
||||
"./styles": "./dist/styles/index.css"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc && tsdown",
|
||||
"dev": "tsdown --watch --no-clean",
|
||||
"check:lint": "oxlint --max-warnings=416 .",
|
||||
"check:types": "tsc --noEmit",
|
||||
"check:format": "oxfmt --check .",
|
||||
"fix:lint": "oxlint --fix .",
|
||||
"fix:format": "oxfmt .",
|
||||
"clean": "rm -rf .turbo && rm -rf .next && rm -rf node_modules && rm -rf dist"
|
||||
},
|
||||
"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/propel": "workspace:*",
|
||||
"@plane/types": "workspace:*",
|
||||
"@plane/ui": "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-document": "^2.22.3",
|
||||
"@tiptap/extension-emoji": "^2.22.3",
|
||||
"@tiptap/extension-heading": "^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": "^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",
|
||||
"buffer": "^6.0.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",
|
||||
"lodash-es": "catalog:",
|
||||
"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/tailwind-config": "workspace:*",
|
||||
"@plane/typescript-config": "workspace:*",
|
||||
"@types/lodash-es": "catalog:",
|
||||
"@types/node": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"postcss": "^8.4.38",
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import postcssConfig from "@plane/tailwind-config/postcss.config.js";
|
||||
|
||||
export default postcssConfig;
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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;
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { Editor } from "@tiptap/core";
|
||||
import { LinkViewContainer } from "@/components/editors/link-view-container";
|
||||
|
||||
export function LinkContainer({
|
||||
editor,
|
||||
containerRef,
|
||||
}: {
|
||||
editor: Editor;
|
||||
containerRef: React.RefObject<HTMLDivElement>;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<LinkViewContainer editor={editor} containerRef={containerRef} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
// helpers
|
||||
import type { TAssetMetaDataRecord } from "@/helpers/assets";
|
||||
// local imports
|
||||
import type { ADDITIONAL_EXTENSIONS } from "./extensions";
|
||||
|
||||
export const ADDITIONAL_ASSETS_META_DATA_RECORD: Partial<Record<ADDITIONAL_EXTENSIONS, TAssetMetaDataRecord>> = {};
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export enum ADDITIONAL_EXTENSIONS {}
|
||||
|
||||
export const ADDITIONAL_BLOCK_NODE_TYPES = [];
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
// plane imports
|
||||
import type { ADDITIONAL_EXTENSIONS } from "@plane/utils";
|
||||
import { 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",
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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 [];
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export * from "./extensions";
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { Extensions } from "@tiptap/core";
|
||||
|
||||
export const CoreEditorAdditionalExtensionsWithoutProps: Extensions = [];
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
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 function DocumentEditorAdditionalExtensions(props: TDocumentEditorAdditionalExtensionsProps) {
|
||||
const { disabledExtensions, flaggedExtensions } = props;
|
||||
|
||||
const documentExtensions = extensionRegistry
|
||||
.filter((config) => config.isEnabled(disabledExtensions, flaggedExtensions))
|
||||
.map((config) => config.getExtension(props));
|
||||
|
||||
return documentExtensions;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export * from "./core";
|
||||
export * from "./document-extensions";
|
||||
export * from "./slash-commands";
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import type { AnyExtension, Extensions } from "@tiptap/core";
|
||||
// extensions
|
||||
import { SlashCommands } from "@/extensions/slash-commands/root";
|
||||
// types
|
||||
import type { 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 function 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;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
// 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;
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { ECustomImageAttributeNames, ECustomImageStatus } from "@/extensions/custom-image/types";
|
||||
|
||||
export type AssetDuplicationContext = {
|
||||
element: Element;
|
||||
originalHtml: string;
|
||||
};
|
||||
|
||||
export type AssetDuplicationResult = {
|
||||
modifiedHtml: string;
|
||||
shouldProcess: boolean;
|
||||
};
|
||||
|
||||
export type AssetDuplicationHandler = (context: AssetDuplicationContext) => AssetDuplicationResult;
|
||||
|
||||
const imageComponentHandler: AssetDuplicationHandler = ({ element, originalHtml }) => {
|
||||
const src = element.getAttribute("src");
|
||||
|
||||
if (!src || src.startsWith("http")) {
|
||||
return { modifiedHtml: originalHtml, shouldProcess: false };
|
||||
}
|
||||
|
||||
// Capture the original HTML BEFORE making any modifications
|
||||
const originalTag = element.outerHTML;
|
||||
|
||||
// Use setAttribute to update attributes
|
||||
const newId = uuidv4();
|
||||
element.setAttribute(ECustomImageAttributeNames.STATUS, ECustomImageStatus.DUPLICATING);
|
||||
element.setAttribute(ECustomImageAttributeNames.ID, newId);
|
||||
|
||||
// Get the modified HTML AFTER the changes
|
||||
const modifiedTag = element.outerHTML;
|
||||
const modifiedHtml = originalHtml.replaceAll(originalTag, modifiedTag);
|
||||
|
||||
return { modifiedHtml, shouldProcess: true };
|
||||
};
|
||||
|
||||
export const assetDuplicationHandlers: Record<string, AssetDuplicationHandler> = {
|
||||
"image-component": imageComponentHandler,
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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;
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export type TAdditionalEditorAsset = never;
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export type TExtendedFileHandler = object;
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export type IEditorExtensionOptions = unknown;
|
||||
|
||||
export type IEditorPropsExtended = unknown;
|
||||
|
||||
export type ICollaborativeDocumentEditorPropsExtended = unknown;
|
||||
|
||||
export type TExtendedEditorCommands = never;
|
||||
|
||||
export type TExtendedCommandExtraProps = unknown;
|
||||
|
||||
export type TExtendedEditorRefApi = unknown;
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
export * from "./issue-embed";
|
||||
export * from "./editor-extended";
|
||||
export * from "./config";
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user