@@ -1,11 +0,0 @@
|
||||
.imex-kanban-card {
|
||||
padding: 0px !important;
|
||||
|
||||
.ant-card-body {
|
||||
padding: 0.8rem;
|
||||
}
|
||||
|
||||
.ant-card-head {
|
||||
padding: 0rem 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,11 +136,14 @@ export default function ProductionBoardCard({ technician, card, bodyshop, cardSe
|
||||
)}
|
||||
</Col>
|
||||
)}
|
||||
<Col span={24}>
|
||||
<div className="ellipses">{`${metadata.v_model_yr || ""} ${
|
||||
metadata.v_make_desc || ""
|
||||
} ${metadata.v_model_desc || ""}`}</div>
|
||||
</Col>
|
||||
{cardSettings && cardSettings.model_info && (
|
||||
<Col span={24}>
|
||||
<div className="ellipses">{`${metadata.v_model_yr || ""} ${
|
||||
metadata.v_make_desc || ""
|
||||
} ${metadata.v_model_desc || ""}`}</div>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
{cardSettings && cardSettings.ins_co_nm && metadata.ins_co_nm && (
|
||||
<Col span={cardSettings && cardSettings.compact ? 24 : 12}>
|
||||
<div className="ellipses">{metadata.ins_co_nm || ""}</div>
|
||||
|
||||
@@ -19,8 +19,10 @@ import ProductionBoardCard from "../production-board-kanban-card/production-boar
|
||||
import ProductionListDetailComponent from "../production-list-detail/production-list-detail.component";
|
||||
import CardColorLegend from "../production-board-kanban-card/production-board-kanban-card-color-legend.component";
|
||||
import "./production-board-kanban.styles.scss";
|
||||
import { createBoardData } from "./production-board-kanban.utils.js";
|
||||
import { createBoardData, createFakeBoardData } from "./production-board-kanban.utils.js";
|
||||
import ProductionBoardKanbanSettings from "./production-board-kanban.settings.component.jsx";
|
||||
import cloneDeep from "lodash/cloneDeep";
|
||||
import isEqual from "lodash/isEqual";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
@@ -63,25 +65,27 @@ export function ProductionBoardKanbanComponent({
|
||||
}, [associationSettings]);
|
||||
|
||||
useEffect(() => {
|
||||
const boardData = createBoardData(
|
||||
const newBoardData = createFakeBoardData(
|
||||
[...bodyshop.md_ro_statuses.production_statuses, ...(bodyshop.md_ro_statuses.additional_board_statuses || [])],
|
||||
data,
|
||||
filter
|
||||
);
|
||||
|
||||
// Build Board Lanes Data
|
||||
boardData.lanes = boardData.lanes.map((lane) => {
|
||||
return {
|
||||
...lane,
|
||||
title: `${lane.title} (${lane.cards.length})`
|
||||
};
|
||||
newBoardData.lanes = newBoardData.lanes.map((lane) => ({
|
||||
...lane,
|
||||
title: `${lane.title} (${lane.cards.length})`
|
||||
}));
|
||||
|
||||
setBoardLanes((prevBoardLanes) => {
|
||||
const deepClonedData = cloneDeep(newBoardData);
|
||||
if (!isEqual(prevBoardLanes, deepClonedData)) {
|
||||
return deepClonedData;
|
||||
}
|
||||
return prevBoardLanes;
|
||||
});
|
||||
|
||||
setBoardLanes(boardData);
|
||||
setIsMoving(false);
|
||||
}, [data, setBoardLanes, setIsMoving, bodyshop.md_ro_statuses, filter]);
|
||||
|
||||
//
|
||||
}, [data, bodyshop.md_ro_statuses, filter]);
|
||||
|
||||
const client = useApolloClient();
|
||||
|
||||
@@ -102,13 +106,11 @@ export function ProductionBoardKanbanComponent({
|
||||
return null;
|
||||
};
|
||||
|
||||
// TODO, refine and use action
|
||||
const onDragEnd = async ({ type, source, destination, draggableId, ...args }) => {
|
||||
// //cardId, sourceLaneId, targetLaneId, position, cardDetails
|
||||
const onDragEnd = async ({ type, source, destination, draggableId }) => {
|
||||
logImEXEvent("kanban_drag_end");
|
||||
|
||||
// Early Gate
|
||||
if (!type || type !== "lane" || !source || !destination) return;
|
||||
// Early gate, also if the card is already moving bail
|
||||
if (!type || type !== "lane" || !source || !destination || isMoving) return;
|
||||
|
||||
setIsMoving(true);
|
||||
|
||||
@@ -120,13 +122,11 @@ export function ProductionBoardKanbanComponent({
|
||||
|
||||
const movedCardWillBeFirst = destination.index === 0;
|
||||
const movedCardWillBeLast = destination.index > targetLane.cards.length - 1;
|
||||
const movedCardIsFirstNewCard = movedCardWillBeFirst && movedCardWillBeLast;
|
||||
|
||||
const lastCardInTargetLane = targetLane.cards[targetLane.cards.length - 1];
|
||||
|
||||
const oldChildCard = sourceLane.cards[destination.index + 1];
|
||||
|
||||
//
|
||||
const newChildCard = movedCardWillBeLast
|
||||
? null
|
||||
: targetLane.cards[
|
||||
@@ -138,7 +138,7 @@ export function ProductionBoardKanbanComponent({
|
||||
];
|
||||
|
||||
const oldChildCardNewParent = oldChildCard ? sourceCard.metadata.kanbanparent : null;
|
||||
//
|
||||
|
||||
let movedCardNewKanbanParent;
|
||||
if (movedCardWillBeFirst) {
|
||||
movedCardNewKanbanParent = "-1";
|
||||
@@ -186,6 +186,7 @@ export function ProductionBoardKanbanComponent({
|
||||
setIsMoving(false);
|
||||
}
|
||||
};
|
||||
|
||||
const totalHrs = useMemo(
|
||||
() =>
|
||||
data
|
||||
|
||||
@@ -51,7 +51,7 @@ export default function ProductionBoardKanbanSettings({ associationSettings, par
|
||||
|
||||
const renderCardSettings = () => (
|
||||
<>
|
||||
<Card title={t("settings.sections.layout")} style={cardStyle}>
|
||||
<Card title={t("production.settings.layout")} style={cardStyle}>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={4}>
|
||||
<Form.Item name="compact" valuePropName="checked">
|
||||
@@ -65,8 +65,13 @@ export default function ProductionBoardKanbanSettings({ associationSettings, par
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
<Card title={t("settings.sections.information")} style={cardStyle}>
|
||||
<Card title={t("production.settings.information")} style={cardStyle}>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={4}>
|
||||
<Form.Item name="model_info" valuePropName="checked">
|
||||
<Checkbox>{t("production.labels.model_info")}</Checkbox>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<Form.Item name="ownr_nm" valuePropName="checked">
|
||||
<Checkbox>{t("production.labels.ownr_nm")}</Checkbox>
|
||||
@@ -119,21 +124,12 @@ export default function ProductionBoardKanbanSettings({ associationSettings, par
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
<Card title={t("settings.sections.beta")} style={cardStyle}>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={4}>
|
||||
<Form.Item name="stickyheader" valuePropName="checked">
|
||||
<Checkbox>{t("production.labels.stickyheader")}</Checkbox>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
|
||||
const renderBoardSettings = () => (
|
||||
<>
|
||||
<Card title={t("settings.sections.layout")} style={cardStyle}>
|
||||
<Card title={t("production.settings.layout")} style={cardStyle}>
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col span={4} style={{ display: "flex", alignItems: "center" }}>
|
||||
<span style={{ marginRight: "8px" }}>Orientation</span>
|
||||
@@ -167,7 +163,7 @@ export default function ProductionBoardKanbanSettings({ associationSettings, par
|
||||
|
||||
const renderLaneSettings = () => (
|
||||
<>
|
||||
<Card title={t("settings.sections.layout")} style={cardStyle}>
|
||||
<Card title={t("production.settings.layout")} style={cardStyle}>
|
||||
<Row gutter={[16, 16]}>
|
||||
{/*<Col span={4}>*/}
|
||||
{/* <Form.Item name="lane_setting_1" valuePropName="checked">*/}
|
||||
@@ -207,13 +203,13 @@ export default function ProductionBoardKanbanSettings({ associationSettings, par
|
||||
<Card>
|
||||
<Form form={form} onFinish={handleFinish} layout="vertical" onValuesChange={handleValuesChange}>
|
||||
<Tabs defaultActiveKey="1">
|
||||
<TabPane tab={t("settings.tabs.card")} key="1">
|
||||
<TabPane tab={t("production.settings.tabs.card")} key="1">
|
||||
{renderCardSettings()}
|
||||
</TabPane>
|
||||
<TabPane tab={t("settings.tabs.board")} key="2">
|
||||
<TabPane tab={t("production.settings.tabs.board")} key="2">
|
||||
{renderBoardSettings()}
|
||||
</TabPane>
|
||||
<TabPane tab={t("settings.tabs.lane")} key="3">
|
||||
<TabPane tab={t("production.settings.tabs.lane")} key="3">
|
||||
{renderLaneSettings()}
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
@@ -236,7 +232,7 @@ export default function ProductionBoardKanbanSettings({ associationSettings, par
|
||||
return (
|
||||
<Popover content={overlay} open={open} placement="topRight">
|
||||
<Button loading={loading} onClick={() => setOpen(!open)}>
|
||||
{t("settings.buttons.boardSettings")}
|
||||
{t("production.settings.board_settings")}
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { groupBy } from "lodash";
|
||||
import fakeData from "./testData/board1200.json";
|
||||
import fakeData from "./testData/board600.json";
|
||||
|
||||
const sortByParentId = (arr) => {
|
||||
// return arr.reduce((accumulator, currentValue) => {
|
||||
|
||||
@@ -8,7 +8,6 @@ import isEqual from "lodash/isEqual";
|
||||
import Lane from "./Lane";
|
||||
import { PopoverWrapper } from "react-popopo";
|
||||
import * as actions from "../../../redux/trello/trello.actions.js";
|
||||
import { isFunction } from "lodash";
|
||||
|
||||
/**
|
||||
* BoardContainer is a React component that represents a Trello-like board.
|
||||
@@ -73,6 +72,8 @@ const BoardContainer = ({
|
||||
...otherProps
|
||||
}) => {
|
||||
const [addLaneMode, setAddLaneMode] = useState(false);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const currentReducerData = useSelector((state) => (state.trello.lanes ? state.trello : {}));
|
||||
@@ -143,7 +144,12 @@ const BoardContainer = ({
|
||||
* onDragStart
|
||||
* @type {(function({draggableId: *, type: *, source: *, mode: *}): void)|*}
|
||||
*/
|
||||
const onDragStart = useCallback(({ draggableId, type, source, mode }) => {}, []);
|
||||
const onDragStart = useCallback(
|
||||
({ draggableId, type, source, mode }) => {
|
||||
setIsDragging(true);
|
||||
},
|
||||
[setIsDragging]
|
||||
);
|
||||
|
||||
/**
|
||||
* onBeforeDragStart
|
||||
@@ -236,8 +242,13 @@ const BoardContainer = ({
|
||||
]
|
||||
);
|
||||
|
||||
const onLaneDrag = ({ draggableId, type, source, reason, mode, destination, combine }) => {
|
||||
const onLaneDrag = async ({ draggableId, type, source, reason, mode, destination, combine }) => {
|
||||
setIsDragging(false);
|
||||
|
||||
if (!type || type !== "lane" || !source || !destination) return;
|
||||
|
||||
setIsProcessing(true);
|
||||
|
||||
dispatch(
|
||||
actions.moveCardAcrossLanes({
|
||||
fromLaneId: source.droppableId,
|
||||
@@ -246,29 +257,26 @@ const BoardContainer = ({
|
||||
index: destination.index
|
||||
})
|
||||
);
|
||||
// onCardMoveAcrossLanes(payload.laneId, laneId, payload.id, addedIndex);
|
||||
};
|
||||
|
||||
const combinedDragEnd = (...params) => {
|
||||
// Early Gate
|
||||
if (!params || !params[0]) return;
|
||||
onLaneDrag(params[0]);
|
||||
if (isFunction(onDragEnd)) {
|
||||
onDragEnd(params[0]);
|
||||
}
|
||||
onDragEnd({ draggableId, type, source, reason, mode, destination, combine })
|
||||
.catch((err) => {
|
||||
console.error("Error in onLaneDrag", err);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsProcessing(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<components.BoardWrapper style={style} orientation={orientation} draggable={false}>
|
||||
<PopoverWrapper>
|
||||
<DragDropContext
|
||||
onDragEnd={combinedDragEnd}
|
||||
onDragEnd={onLaneDrag}
|
||||
onDragUpdate={onDragUpdate}
|
||||
onDragStart={onDragStart}
|
||||
onBeforeDragStart={onBeforeDragStart}
|
||||
onBeforeCapture={onBeforeCapture}
|
||||
contextId={groupName}
|
||||
// getChildPayload={(index) => getLaneDetails(index)}
|
||||
>
|
||||
{currentReducerData.lanes.map((lane, index) => {
|
||||
const { id, droppable, ...laneOtherProps } = lane;
|
||||
@@ -288,6 +296,8 @@ const BoardContainer = ({
|
||||
{...laneOtherProps}
|
||||
{...passThroughProps}
|
||||
cards={lane.cards}
|
||||
isDragging={isDragging}
|
||||
isProcessing={isProcessing}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -11,12 +11,14 @@ import { Draggable, Droppable } from "../dnd/lib";
|
||||
import { Virtuoso, VirtuosoGrid } from "react-virtuoso";
|
||||
import HeightPreservingItem from "../components/Lane/HeightPreservingItem.jsx";
|
||||
|
||||
function Lane({
|
||||
const Lane = ({
|
||||
actions,
|
||||
id,
|
||||
boardId,
|
||||
title,
|
||||
index,
|
||||
isDragging,
|
||||
isProcessing,
|
||||
laneSortFunction,
|
||||
style = {},
|
||||
cardStyle = {},
|
||||
@@ -51,7 +53,7 @@ function Lane({
|
||||
className,
|
||||
currentPage,
|
||||
...otherProps
|
||||
}) {
|
||||
}) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [currentPageFinal, setCurrentPageFinal] = useState(currentPage);
|
||||
const [addCardMode, setAddCardMode] = useState(false);
|
||||
@@ -99,77 +101,80 @@ function Lane({
|
||||
};
|
||||
}, [handleScroll]);
|
||||
|
||||
const sortCards = (cards, sortFunction) => {
|
||||
const sortCards = useCallback((cards, sortFunction) => {
|
||||
if (!cards) return [];
|
||||
if (!sortFunction) return cards;
|
||||
return cards.concat().sort((card1, card2) => sortFunction(card1, card2));
|
||||
};
|
||||
}, []);
|
||||
|
||||
const removeCard = (cardId) => {
|
||||
if (onBeforeCardDelete && typeof onBeforeCardDelete === "function") {
|
||||
onBeforeCardDelete(() => {
|
||||
const removeCard = useCallback(
|
||||
(cardId) => {
|
||||
if (onBeforeCardDelete && typeof onBeforeCardDelete === "function") {
|
||||
onBeforeCardDelete(() => {
|
||||
onCardDelete && onCardDelete(cardId, id);
|
||||
actions.removeCard({ laneId: id, cardId: cardId });
|
||||
});
|
||||
} else {
|
||||
onCardDelete && onCardDelete(cardId, id);
|
||||
actions.removeCard({ laneId: id, cardId: cardId });
|
||||
});
|
||||
} else {
|
||||
onCardDelete && onCardDelete(cardId, id);
|
||||
actions.removeCard({ laneId: id, cardId: cardId });
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
[onBeforeCardDelete, onCardDelete, actions, id]
|
||||
);
|
||||
|
||||
const handleCardClick = (e, card) => {
|
||||
onCardClick && onCardClick(card.id, card.metadata, card.laneId);
|
||||
e.stopPropagation();
|
||||
};
|
||||
const handleCardClick = useCallback(
|
||||
(e, card) => {
|
||||
onCardClick && onCardClick(card.id, card.metadata, card.laneId);
|
||||
e.stopPropagation();
|
||||
},
|
||||
[onCardClick]
|
||||
);
|
||||
|
||||
const showEditableCard = () => {
|
||||
const showEditableCard = useCallback(() => {
|
||||
setAddCardMode(true);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const hideEditableCard = () => {
|
||||
const hideEditableCard = useCallback(() => {
|
||||
setAddCardMode(false);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const addNewCard = (params) => {
|
||||
const laneId = id;
|
||||
const newCardId = v1();
|
||||
hideEditableCard();
|
||||
let card = { id: newCardId, ...params };
|
||||
actions.addCard({ laneId, card });
|
||||
onCardAdd(card, laneId);
|
||||
};
|
||||
const addNewCard = useCallback(
|
||||
(params) => {
|
||||
const laneId = id;
|
||||
const newCardId = v1();
|
||||
hideEditableCard();
|
||||
let card = { id: newCardId, ...params };
|
||||
actions.addCard({ laneId, card });
|
||||
onCardAdd(card, laneId);
|
||||
},
|
||||
[actions, id, onCardAdd, hideEditableCard]
|
||||
);
|
||||
|
||||
// const onDragStart = ({ payload }) => {
|
||||
// handleDragStart && handleDragStart(payload.id, payload.laneId);
|
||||
// };
|
||||
const updateCard = useCallback(
|
||||
(updatedCard) => {
|
||||
actions.updateCard({ laneId: id, card: updatedCard });
|
||||
onCardUpdate(id, updatedCard);
|
||||
},
|
||||
[actions, id, onCardUpdate]
|
||||
);
|
||||
|
||||
// const shouldAcceptDrop = (sourceContainerOptions) => {
|
||||
// return droppable && sourceContainerOptions.groupName === groupName;
|
||||
// };
|
||||
|
||||
const updateCard = (updatedCard) => {
|
||||
actions.updateCard({ laneId: id, card: updatedCard });
|
||||
onCardUpdate(id, updatedCard);
|
||||
};
|
||||
|
||||
const removeLane = () => {
|
||||
const removeLane = useCallback(() => {
|
||||
actions.removeLane({ laneId: id });
|
||||
onLaneDelete(id);
|
||||
};
|
||||
}, [actions, id, onLaneDelete]);
|
||||
|
||||
const updateTitle = (value) => {
|
||||
actions.updateLane({ id, title: value });
|
||||
onLaneUpdate(id, { title: value });
|
||||
};
|
||||
const updateTitle = useCallback(
|
||||
(value) => {
|
||||
actions.updateLane({ id, title: value });
|
||||
onLaneUpdate(id, { title: value });
|
||||
},
|
||||
[actions, id, onLaneUpdate]
|
||||
);
|
||||
|
||||
const toggleLaneCollapsed = () => {
|
||||
const toggleLaneCollapsed = useCallback(() => {
|
||||
collapsibleLanes && setCollapsed(!collapsed);
|
||||
};
|
||||
}, [collapsibleLanes, collapsed]);
|
||||
|
||||
/**
|
||||
* Card component
|
||||
* @type {React.NamedExoticComponent<{readonly item?: *, readonly provided?: *, readonly isDragging?: *}>}
|
||||
*/
|
||||
const Card = React.memo(({ provided, item: card, isDragging }) => {
|
||||
const onDeleteCard = () => removeCard(card.id);
|
||||
return (
|
||||
@@ -210,27 +215,16 @@ function Lane({
|
||||
</Draggable>
|
||||
);
|
||||
|
||||
/**
|
||||
* Render the add card link
|
||||
* @returns {false|React.JSX.Element}
|
||||
*/
|
||||
const renderAddCardLink = () =>
|
||||
editable && !addCardMode && <components.AddCardLink onClick={showEditableCard} laneId={id} />;
|
||||
const renderAddCardLink = useCallback(
|
||||
() => editable && !addCardMode && <components.AddCardLink onClick={showEditableCard} laneId={id} />,
|
||||
[editable, addCardMode, showEditableCard, id]
|
||||
);
|
||||
|
||||
/**
|
||||
* Render the new card form
|
||||
* @returns {false|React.JSX.Element}
|
||||
*/
|
||||
const renderNewCardForm = () =>
|
||||
addCardMode && <components.NewCardForm onCancel={hideEditableCard} laneId={id} onAdd={addNewCard} />;
|
||||
const renderNewCardForm = useCallback(
|
||||
() => addCardMode && <components.NewCardForm onCancel={hideEditableCard} laneId={id} onAdd={addNewCard} />,
|
||||
[addCardMode, hideEditableCard, addNewCard, id]
|
||||
);
|
||||
|
||||
/**
|
||||
* Wrapper for the item in the grid layout
|
||||
* @param children
|
||||
* @param props
|
||||
* @returns {React.JSX.Element}
|
||||
* @constructor
|
||||
*/
|
||||
const ItemWrapper = ({ children, ...props }) => (
|
||||
<div
|
||||
{...props}
|
||||
@@ -401,12 +395,11 @@ function Lane({
|
||||
>
|
||||
{renderHeader({ id, cards, ...passedProps })}
|
||||
{renderDragContainer()}
|
||||
{/*{renderDragContainer(isDraggingOver)}*/}
|
||||
{loading && <components.Loader />}
|
||||
{showFooter && <components.LaneFooter onClick={toggleLaneCollapsed} collapsed={collapsed} />}
|
||||
</components.Section>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Lane.propTypes = {
|
||||
actions: PropTypes.object,
|
||||
|
||||
@@ -100,8 +100,6 @@ const LaneHelper = {
|
||||
// Clone the state to avoid mutation
|
||||
const newLanes = cloneDeep(state.lanes);
|
||||
|
||||
console.dir({ fromLaneId, toLaneId, cardId, index });
|
||||
|
||||
// Find the source and destination lanes using the lane IDs
|
||||
const fromLane = newLanes.find((lane) => lane.id === fromLaneId);
|
||||
const toLane = newLanes.find((lane) => lane.id === toLaneId);
|
||||
|
||||
@@ -81,6 +81,9 @@ export const StyleHorizontal = styled.div`
|
||||
.react-trello-lane.lane-collapsed {
|
||||
min-height: 15px;
|
||||
}
|
||||
.ant-card-body {
|
||||
padding: 4px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const StyleVertical = styled.div`
|
||||
@@ -98,6 +101,7 @@ export const StyleVertical = styled.div`
|
||||
|
||||
.react-trello-card {
|
||||
flex: 0 1 auto;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.react-trello-board {
|
||||
@@ -109,6 +113,9 @@ export const StyleVertical = styled.div`
|
||||
|
||||
.grid-item {
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
}
|
||||
`;
|
||||
|
||||
export const CustomPopoverContainer = styled(PopoverContainer)`
|
||||
|
||||
@@ -2719,6 +2719,16 @@
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
"settings": {
|
||||
"layout": "Layout",
|
||||
"information": "Information",
|
||||
"board_settings": "Board Settings",
|
||||
"tabs": {
|
||||
"card": "Card",
|
||||
"board": "Board",
|
||||
"lane": "Lane"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
"addcolumns": "Add Columns",
|
||||
"bodypriority-clear": "Clear Body Priority",
|
||||
|
||||
Reference in New Issue
Block a user