IO-1533 Kanban Customization

This commit is contained in:
Patrick Fic
2021-11-15 13:53:54 -08:00
parent efc2844d99
commit c129b5ba8c
15 changed files with 322 additions and 22 deletions

View File

@@ -34961,6 +34961,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>cardsettings</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>detailpriority</name>
<definition_loaded>false</definition_loaded>

View File

@@ -5,9 +5,9 @@
"proxy": "http://localhost:4000",
"dependencies": {
"@apollo/client": "^3.4.17",
"@asseinfo/react-kanban": "^2.2.0",
"@craco/craco": "^6.4.0",
"@fingerprintjs/fingerprintjs": "^3.3.0",
"@lourenci/react-kanban": "^2.1.0",
"@openreplay/tracker": "^3.4.7",
"@openreplay/tracker-assist": "^3.4.4",
"@openreplay/tracker-graphql": "^3.0.0",

View File

@@ -10,7 +10,12 @@ import { useTranslation } from "react-i18next";
import ProductionSubletsManageComponent from "../production-sublets-manage/production-sublets-manage.component";
import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component";
export default function ProductionBoardCard(technician, card, bodyshop) {
export default function ProductionBoardCard(
technician,
card,
bodyshop,
cardSettings
) {
const { t } = useTranslation();
const menu = (
<div>
@@ -39,7 +44,6 @@ export default function ProductionBoardCard(technician, card, bodyshop) {
<Dropdown overlay={menu} trigger={["contextMenu"]}>
<Card
className="react-kanban-card imex-kanban-card tight-antd-rows"
//style={{ margin: ".2rem 0rem" }}
title={`${card.ro_number || t("general.labels.na")} - ${
card.v_model_yr
} ${card.v_make_desc || ""} ${card.v_model_desc || ""}`}

View File

@@ -0,0 +1,78 @@
import React, { useEffect } from "react";
import { Form, Dropdown, Card, Button, Switch } from "antd";
import { useTranslation } from "react-i18next";
import { useMutation } from "@apollo/client";
import { UPDATE_KANBAN_SETTINGS } from "../../graphql/user.queries";
import { update } from "lodash";
export default function ProductionBoardKanbanCardSettings({
associationSettings,
}) {
const [form] = Form.useForm();
const [updateKbSettings] = useMutation(UPDATE_KANBAN_SETTINGS);
useEffect(() => {
form.setFieldsValue(
associationSettings && associationSettings.kanban_settings
);
}, [form, associationSettings]);
const { t } = useTranslation();
const handleFinish = async (values) => {
const result = await updateKbSettings({
variables: {
id: associationSettings && associationSettings.id,
ks: values,
},
});
};
const overlay = (
<div>
<Card>
<Form form={form} onFinish={handleFinish}>
<Form.Item
label={t("production.labels.compact")}
name="compact"
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("production.labels.laborhrs")}
name="laborhrs"
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("production.labels.employeeassignments")}
name="employeeassignments"
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("production.labels.alert")}
name="alert"
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item
label={t("production.labels.sublets")}
name="sublets"
valuePropName="checked"
>
<Switch />
</Form.Item>
</Form>
</Card>
</div>
);
return (
<Dropdown trigger="click" overlay={overlay}>
<Button>{t("production.labels.cardsettings")}</Button>
</Dropdown>
);
}

View File

@@ -1,6 +1,7 @@
import { useApolloClient } from "@apollo/client";
import Board, { moveCard } from "@lourenci/react-kanban";
import "@lourenci/react-kanban/dist/styles.css";
import Board, { moveCard } from "@asseinfo/react-kanban";
//import "@asseinfo/react-kanban/dist/styles.css";
import "./production-board-kanban.styles.scss";
import { notification, PageHeader, Space, Statistic } from "antd";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -16,6 +17,7 @@ import ProductionBoardFilters from "../production-board-filters/production-board
import { selectTechnician } from "../../redux/tech/tech.selectors";
import { insertAuditTrail } from "../../redux/application/application.actions";
import AuditTrailMapping from "../../utils/AuditTrailMappings";
import ProductionBoardKanbanCardSettings from "./production-board-kanban.card-settings.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -32,6 +34,7 @@ export function ProductionBoardKanbanComponent({
bodyshop,
technician,
insertAuditTrail,
associationSettings,
}) {
const [boardLanes, setBoardLanes] = useState({
columns: [{ id: "Loading...", title: "Loading...", cards: [] }],
@@ -116,6 +119,7 @@ export function ProductionBoardKanbanComponent({
newChildCard ? newChildCard.id : null,
newChildCardNewParent
),
// TODO: optimisticResponse
});
insertAuditTrail({
jobid: card.id,
@@ -130,6 +134,7 @@ export function ProductionBoardKanbanComponent({
});
}
};
const totalHrs = data
.reduce(
(acc, val) =>
@@ -139,6 +144,7 @@ export function ProductionBoardKanbanComponent({
0
)
.toFixed(1);
return (
<div>
<IndefiniteLoading loading={isMoving} />
@@ -156,18 +162,30 @@ export function ProductionBoardKanbanComponent({
</Space>
}
extra={
<ProductionBoardFilters
filter={filter}
setFilter={setFilter}
loading={isMoving}
/>
<Space wrap>
<ProductionBoardFilters
filter={filter}
setFilter={setFilter}
loading={isMoving}
/>
<ProductionBoardKanbanCardSettings
associationSettings={associationSettings}
/>
</Space>
}
/>
<Board
children={boardLanes}
disableCardDrag={isMoving}
renderCard={(card) => ProductionBoardCard(technician, card, bodyshop)}
renderCard={(card) =>
ProductionBoardCard(
technician,
card,
bodyshop,
associationSettings.kanban_settings
)
}
onCardDragEnd={handleDragEnd}
/>
</div>

View File

@@ -1,25 +1,41 @@
import { useSubscription } from "@apollo/client";
import { useQuery, useSubscription } from "@apollo/client";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { getCurrentUser } from "../../firebase/firebase.utils";
import { SUBSCRIPTION_JOBS_IN_PRODUCTION } from "../../graphql/jobs.queries";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { QUERY_KANBAN_SETTINGS } from "../../graphql/user.queries";
import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import ProductionBoardKanbanComponent from "./production-board-kanban.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
});
export function ProductionBoardKanbanContainer({ bodyshop }) {
export function ProductionBoardKanbanContainer({ bodyshop, currentUser }) {
const { loading, data } = useSubscription(
SUBSCRIPTION_JOBS_IN_PRODUCTION,
{}
);
const { loading: associationSettingsLoading, data: associationSettings } =
useQuery(QUERY_KANBAN_SETTINGS, {
variables: { email: currentUser.email },
});
return (
<ProductionBoardKanbanComponent
loading={loading}
loading={loading || associationSettingsLoading}
data={data ? data.jobs : []}
associationSettings={
associationSettings && associationSettings.associations[0]
? associationSettings.associations[0]
: null
}
/>
);
}

View File

@@ -0,0 +1,130 @@
.react-kanban-board {
padding: 5px;
}
.react-kanban-card {
border-radius: 3px;
background-color: #fff;
padding: 10px;
margin-bottom: 7px;
}
.react-kanban-card-skeleton,
.react-kanban-card,
.react-kanban-card-adder-form {
box-sizing: border-box;
max-width: 250px;
min-width: 250px;
}
.react-kanban-card-skeleton-compact,
.react-kanban-card-compact,
.react-kanban-card-adder-form-compact {
box-sizing: border-box;
max-width: 120px;
min-width: 120px;
}
.react-kanban-card--dragging {
box-shadow: 2px 2px grey;
}
.react-kanban-card__description {
padding-top: 10px;
}
.react-kanban-card__title {
border-bottom: 1px solid #eee;
padding-bottom: 5px;
font-weight: bold;
display: flex;
justify-content: space-between;
}
.react-kanban-column {
padding: 15px;
border-radius: 2px;
background-color: #eee;
margin: 5px;
}
.react-kanban-column input:focus {
outline: none;
}
.react-kanban-card-adder-form {
border-radius: 3px;
background-color: #fff;
padding: 10px;
margin-bottom: 7px;
}
.react-kanban-card-adder-form input {
border: 0px;
font-family: inherit;
font-size: inherit;
}
.react-kanban-card-adder-button {
width: 100%;
margin-top: 5px;
background-color: transparent;
cursor: pointer;
border: 1px solid #ccc;
transition: 0.3s;
border-radius: 3px;
font-size: 20px;
margin-bottom: 10px;
font-weight: bold;
}
.react-kanban-card-adder-button:hover {
background-color: #ccc;
}
.react-kanban-card-adder-form__title {
font-weight: bold;
border-bottom: 1px solid #eee;
padding-bottom: 5px;
font-weight: bold;
display: flex;
justify-content: space-between;
width: 100%;
padding: 0px;
}
.react-kanban-card-adder-form__title:focus {
outline: none;
}
.react-kanban-card-adder-form__description {
width: 100%;
margin-top: 10px;
}
.react-kanban-card-adder-form__description:focus {
outline: none;
}
.react-kanban-card-adder-form__button {
background-color: #eee;
border: none;
padding: 5px;
width: 45%;
margin-top: 5px;
border-radius: 3px;
}
.react-kanban-card-adder-form__button:hover {
transition: 0.3s;
cursor: pointer;
background-color: #ccc;
}
.react-kanban-column-header {
padding-bottom: 10px;
font-weight: bold;
}
.react-kanban-column-header input:focus {
outline: none;
}
.react-kanban-column-header__button {
color: #333333;
background-color: #ffffff;
border-color: #cccccc;
}
.react-kanban-column-header__button:hover,
.react-kanban-column-header__button:focus,
.react-kanban-column-header__button:active {
background-color: #e6e6e6;
}
.react-kanban-column-adder-button {
border: 2px dashed #eee;
height: 132px;
margin: 5px;
}
.react-kanban-column-adder-button:hover {
cursor: pointer;
}

View File

@@ -69,3 +69,25 @@ export const UPDATE_FCM_TOKEN = gql`
}
}
`;
export const QUERY_KANBAN_SETTINGS = gql`
query QUERY_KANBAN_SETTINGS($email: String!) {
associations(
where: { _and: { useremail: { _eq: $email }, active: { _eq: true } } }
) {
id
kanban_settings
}
}
`;
export const UPDATE_KANBAN_SETTINGS = gql`
mutation UPDATE_KANBAN_SETTINGS($id: uuid!, $ks: jsonb) {
update_associations_by_pk(
pk_columns: { id: $id }
_set: { kanban_settings: $ks }
) {
id
kanban_settings
}
}
`;

View File

@@ -2085,6 +2085,7 @@
"alerton": "Add alert to job",
"bodyhours": "B",
"bodypriority": "B/P",
"cardsettings": "Card Settings",
"detailpriority": "D/P",
"employeesearch": "Employee Search",
"jobdetail": "Job Details",

View File

@@ -2085,6 +2085,7 @@
"alerton": "",
"bodyhours": "",
"bodypriority": "",
"cardsettings": "",
"detailpriority": "",
"employeesearch": "",
"jobdetail": "",

View File

@@ -2085,6 +2085,7 @@
"alerton": "",
"bodyhours": "",
"bodypriority": "",
"cardsettings": "",
"detailpriority": "",
"employeesearch": "",
"jobdetail": "",

View File

@@ -54,6 +54,13 @@
tslib "^2.3.0"
zen-observable-ts "~1.1.0"
"@asseinfo/react-kanban@^2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@asseinfo/react-kanban/-/react-kanban-2.2.0.tgz#56b703e9e6d1722a4b2edee321c928fd23749a4d"
integrity sha512-/gCigrNXRHeP9VCo8RipTOrA0vAPRIOThJhR4ibVxe6BLkaWFUEuJ1RMT4fODpRRsE3XsdrfVGKkfpRBKgvxXg==
dependencies:
react-beautiful-dnd "^13.0.0"
"@babel/code-frame@7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
@@ -1977,13 +1984,6 @@
"@types/yargs" "^15.0.0"
chalk "^4.0.0"
"@lourenci/react-kanban@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@lourenci/react-kanban/-/react-kanban-2.1.0.tgz#1e9ffab30d37a19815adcb1a94cb35817ebee8a9"
integrity sha512-VZK+HbH2DtfQA7RhMWIZAtieN78n6XzJ8EDkKdaOxbefg1FYEJeyQ/GCav6CW7UvWEIAJxBjaYwF+csadIMYFA==
dependencies:
react-beautiful-dnd "^13.0.0"
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"

View File

@@ -203,6 +203,7 @@
- authlevel
- default_prod_list_view
- id
- kanban_settings
- qbo_realmId
- shopid
- useremail
@@ -217,6 +218,7 @@
- active
- authlevel
- default_prod_list_view
- kanban_settings
- qbo_realmId
filter:
bodyshop:

View File

@@ -0,0 +1,4 @@
-- Could not auto-generate a down migration.
-- Please write an appropriate down migration for the SQL below:
-- alter table "public"."associations" add column "kanban_settings" jsonb
-- null default jsonb_build_object();

View File

@@ -0,0 +1,2 @@
alter table "public"."associations" add column "kanban_settings" jsonb
null default jsonb_build_object();