Merge pull request #8 from snaptsoft/dev-patrick

Dev patrick
This commit is contained in:
2020-02-06 16:41:18 -08:00
committed by GitHub
138 changed files with 6090 additions and 128 deletions

View File

@@ -1,5 +1,35 @@
**Required items**
-Bodyshop Record
-Counter Record - type: ronum
..\*Include the statuses file in the format of:
```json
"statuses": [
"Open",
"Scheduled",
"Arrived",
"Repair Plan",
"Parts",
"Body",
"Prep",
"Paint",
"Reassembly",
"Sublet",
"Detail",
"Completed",
"Delivered",
"Invoiced",
"Exported"
],
"default_imported": "Open",
"default_scheduled": "Scheduled",
"default_arrived": "Arrived",
"default_completed": "Completed",
"default_delivered": "Delivered",
"default_invoiced": "Invoiced",
"default_exported": "Exported"
}
```
--\* Set the region for the shop.
-Counter Record - type: ronum

View File

@@ -1,4 +1,4 @@
<babeledit_project version="1.2" be_version="2.6.1">
<babeledit_project be_version="2.6.1" version="1.2">
<!--
BabelEdit project file
@@ -18,6 +18,439 @@
<folder_node>
<name>translation</name>
<children>
<folder_node>
<name>appointments</name>
<children>
<folder_node>
<name>actions</name>
<children>
<concept_node>
<name>cancel</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>intake</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>reschedule</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>viewjob</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>
</children>
</folder_node>
<folder_node>
<name>errors</name>
<children>
<concept_node>
<name>canceling</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>saving</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>
</children>
</folder_node>
<folder_node>
<name>labels</name>
<children>
<concept_node>
<name>arrivedon</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>cancelledappointment</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>nodateselected</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>priorappointments</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>scheduledfor</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>
</children>
</folder_node>
<folder_node>
<name>successes</name>
<children>
<concept_node>
<name>canceled</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>created</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>
</children>
</folder_node>
</children>
</folder_node>
<folder_node>
<name>associations</name>
<children>
<folder_node>
<name>actions</name>
<children>
<concept_node>
<name>activate</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>
</children>
</folder_node>
<folder_node>
<name>fields</name>
<children>
<concept_node>
<name>active</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>shopname</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>
</children>
</folder_node>
<folder_node>
<name>labels</name>
<children>
<concept_node>
<name>actions</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>
</children>
</folder_node>
</children>
</folder_node>
<folder_node>
<name>bodyshop</name>
<children>
<folder_node>
<name>errors</name>
<children>
<concept_node>
<name>loading</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>
</children>
</folder_node>
</children>
</folder_node>
<folder_node>
<name>documents</name>
<children>
@@ -667,6 +1100,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>changestatus</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>convert</name>
<definition_loaded>false</definition_loaded>
@@ -730,6 +1184,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>schedule</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>
</children>
</folder_node>
<folder_node>
@@ -1875,7 +2350,7 @@
</translations>
</concept_node>
<concept_node>
<name>pay_date</name>
<name>ownr_ph1</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
@@ -1896,7 +2371,7 @@
</translations>
</concept_node>
<concept_node>
<name>phone1</name>
<name>pay_date</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
@@ -2887,6 +3362,27 @@
<folder_node>
<name>labels</name>
<children>
<concept_node>
<name>appointmentconfirmation</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>available_new_jobs</name>
<definition_loaded>false</definition_loaded>
@@ -3584,6 +4080,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>schedule</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>
</children>
</folder_node>
<folder_node>
@@ -4407,6 +4924,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>schedule</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>
</children>
</folder_node>
<folder_node>
@@ -4436,6 +4974,53 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>updateprofile</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>
</children>
</folder_node>
<folder_node>
<name>fields</name>
<children>
<concept_node>
<name>displayname</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>
</children>
</folder_node>
</children>

View File

@@ -19,6 +19,7 @@
"node-sass": "^4.13.0",
"react": "^16.12.0",
"react-apollo": "^3.1.3",
"react-big-calendar": "^0.23.0",
"react-chartjs-2": "^2.8.0",
"react-dom": "^16.12.0",
"react-i18next": "^11.2.7",

View File

@@ -14,8 +14,6 @@ import SpinnerComponent from "../components/loading-spinner/loading-spinner.comp
import errorLink from "../graphql/apollo-error-handling";
import App from "./App";
class AppContainer extends Component {
state = {
client: null,

View File

@@ -28,19 +28,19 @@ const mapDispatchToProps = dispatch => ({
export default connect(
mapStateToProps,
mapDispatchToProps
)(({ checkUserSession, currentUser }) => {
)(({ checkUserSession, currentUser, setBodyshop }) => {
useEffect(() => {
checkUserSession();
return () => {};
}, [checkUserSession]);
if (false)
i18next.changeLanguage("en_US", (err, t) => {
if (currentUser && currentUser.language)
i18next.changeLanguage(currentUser.language, (err, t) => {
if (err)
return console.log("Error encountered when changing languages.", err);
});
console.log("currentUser", currentUser);
if (currentUser.authorized === null) {
//TODO: Translate this.
return <LoadingSpinner message="Waiting for Current Auth to persist." />;

View File

@@ -1,32 +1,34 @@
import React, { useState } from "react";
import ChatWindowComponent from "./chat-window.component";
import { Button } from "antd";
import { Layout } from "antd";
import React from "react";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectCurrentUser } from "../../redux/user/user.selectors";
import { toggleChatVisible } from "../../redux/messaging/messaging.actions";
import { selectChatVisible } from "../../redux/messaging/messaging.selectors";
import ChatWindowComponent from "./chat-window.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
chatVisible: selectChatVisible
});
const mapDispatchToProps = dispatch => ({
// signOutStart: () => dispatch(signOutStart())
toggleChatVisible: () => dispatch(toggleChatVisible())
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(function ChatWindowContainer() {
const [visible, setVisible] = useState(false);
)(function ChatWindowContainer({ chatVisible, toggleChatVisible }) {
return (
<div>
<Button onClick={() => setVisible(!visible)}>Drawer!</Button>
<ChatWindowComponent
mask={false}
maskClosable={false}
visible={visible}
zIndex={0}
/>
</div>
<Layout.Sider
collapsible
defaultCollapsed
theme="light"
collapsedWidth={0}
width={300}
collapsed={!chatVisible}
onCollapse={(collapsed, type) => toggleChatVisible()}
>
<ChatWindowComponent mask={false} maskClosable={false} zIndex={0} />
</Layout.Sider>
);
});

View File

@@ -29,6 +29,12 @@ export default ({ landingHeader, selectedNavItem }) => {
</Link>
</Menu.Item>
<Menu.SubMenu title={t("menus.header.jobs")}>
<Menu.Item key="schedule">
<Link to="/manage/schedule">
<Icon type="calendar" />
{t("menus.header.schedule")}
</Link>
</Menu.Item>
<Menu.Item key="activejobs">
<Link to="/manage/jobs">
<Icon type="home" />

View File

@@ -18,8 +18,6 @@ import JobDetailCardsPartsComponent from "./job-detail-cards.parts.component";
import "./job-detail-cards.styles.scss";
import JobDetailCardsTotalsComponent from "./job-detail-cards.totals.component";
export default function JobDetailCards({ selectedJob }) {
const { loading, error, data, refetch } = useQuery(QUERY_JOB_CARD_DETAILS, {
fetchPolicy: "network-only",
@@ -33,10 +31,10 @@ export default function JobDetailCards({ selectedJob }) {
return <div>{t("jobs.errors.nojobselected")}</div>;
}
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type='error' />;
if (error) return <AlertComponent message={error.message} type="error" />;
return (
<div className='job-cards-container'>
<div className="job-cards-container">
<NoteUpsertModal
jobId={data.jobs_by_pk.id}
visible={noteModalVisible}
@@ -47,9 +45,9 @@ export default function JobDetailCards({ selectedJob }) {
ghost={false}
onBack={() => window.history.back()}
tags={
<span key='job-status'>
{data.jobs_by_pk.job_status ? (
<Tag color='blue'>{data.jobs_by_pk.job_status.name}</Tag>
<span key="job-status">
{data.jobs_by_pk.status ? (
<Tag color="blue">{data.jobs_by_pk.status}</Tag>
) : null}
</span>
}
@@ -68,31 +66,34 @@ export default function JobDetailCards({ selectedJob }) {
}
extra={[
<Link
key='documents'
to={`/manage/jobs/${data.jobs_by_pk.id}#documents`}>
key="documents"
to={`/manage/jobs/${data.jobs_by_pk.id}#documents`}
>
<Button>
<Icon type='file-image' />
<Icon type="file-image" />
{t("jobs.actions.addDocuments")}
</Button>
</Link>,
<Button key='printing'>
<Icon type='printer' />
<Button key="printing">
<Icon type="printer" />
{t("jobs.actions.printCenter")}
</Button>,
<Button
key='notes'
actiontype='addNote'
key="notes"
actiontype="addNote"
onClick={() => {
setNoteModalVisible(!noteModalVisible);
}}>
<Icon type='edit' />
}}
>
<Icon type="edit" />
{t("jobs.actions.addNote")}
</Button>,
<Button key='postinvoices'>
<Icon type='shopping-cart' />
<Button key="postinvoices">
<Icon type="shopping-cart" />
{t("jobs.actions.postInvoices")}
</Button>
]}>
]}
>
{
// loading ? (
// <LoadingSkeleton />
@@ -113,7 +114,7 @@ export default function JobDetailCards({ selectedJob }) {
// )
}
<section className='job-cards'>
<section className="job-cards">
<JobDetailCardsCustomerComponent
loading={loading}
data={data ? data.jobs_by_pk : null}

View File

@@ -1,8 +1,12 @@
import {
Avatar,
Badge,
Button,
Checkbox,
Descriptions,
Dropdown,
Icon,
Menu,
notification,
PageHeader,
Tag
@@ -10,17 +14,31 @@ import {
import React from "react";
import { useTranslation } from "react-i18next";
import Moment from "react-moment";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { createStructuredSelector } from "reselect";
import CarImage from "../../assets/car.svg";
import { selectBodyshop } from "../../redux/user/user.selectors";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
export default function JobsDetailHeader({
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
export default connect(
mapStateToProps,
null
)(function JobsDetailHeader({
job,
mutationConvertJob,
refetch,
handleSubmit
handleSubmit,
scheduleModalState,
bodyshop,
updateJobStatus
}) {
const { t } = useTranslation();
const setscheduleModalVisible = scheduleModalState[1];
const tombstoneTitle = (
<div>
@@ -57,7 +75,34 @@ export default function JobsDetailHeader({
</div>
);
const statusmenu = (
<Menu
onClick={e => {
updateJobStatus(e.key);
}}
>
{bodyshop.md_ro_statuses.statuses.map(item => (
<Menu.Item key={item}>{item}</Menu.Item>
))}
</Menu>
);
const menuExtra = [
<Dropdown overlay={statusmenu} key="changestatus">
<Button>
{t("jobs.actions.changestatus")} <Icon type="down" />
</Button>
</Dropdown>,
<Badge key="schedule" count={job.appointments_aggregate.aggregate.count}>
<Button
//TODO: Enabled logic based on status.
onClick={() => {
setscheduleModalVisible(true);
}}
>
{t("jobs.actions.schedule")}
</Button>
</Badge>,
<Button
key="convert"
type="dashed"
@@ -95,9 +140,7 @@ export default function JobsDetailHeader({
subTitle={tombstoneSubtitle}
tags={
<span key="job-status">
{job.job_status ? (
<Tag color="blue">{job.job_status.name}</Tag>
) : null}
{job.status ? <Tag color="blue">{job.status}</Tag> : null}
</span>
}
extra={menuExtra}
@@ -127,4 +170,4 @@ export default function JobsDetailHeader({
</Descriptions>
</PageHeader>
);
}
});

View File

@@ -5,6 +5,7 @@ import { Link } from "react-router-dom";
import PhoneFormatter from "../../utils/PhoneFormatter";
import { alphaSort } from "../../utils/sorters";
import { withRouter } from "react-router-dom";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
export default withRouter(function JobsList({
loading,
@@ -56,14 +57,12 @@ export default withRouter(function JobsList({
</Link>
) : (
// t("jobs.errors.noowner")
<span>
{record.ownr_fn} {record.ownr_ln}
</span>
<span>{`${record.ownr_fn} ${record.ownr_ln}`}</span>
);
}
},
{
title: t("jobs.fields.phone1"),
title: t("jobs.fields.ownr_ph1"),
dataIndex: "ownr_ph1",
key: "ownr_ph1",
width: "12%",
@@ -95,7 +94,7 @@ export default withRouter(function JobsList({
sortOrder:
state.sortedInfo.columnKey === "status" && state.sortedInfo.order,
render: (text, record) => {
return record.job_status?.name || t("general.labels.na");
return record.status || t("general.labels.na");
}
},
@@ -154,13 +153,13 @@ export default withRouter(function JobsList({
title: t("jobs.fields.clm_total"),
dataIndex: "clm_total",
key: "clm_total",
width: "8%",
width: "10%",
sorter: (a, b) => a.clm_total - b.clm_total,
sortOrder:
state.sortedInfo.columnKey === "clm_total" && state.sortedInfo.order,
render: (text, record) => {
return record.clm_total ? (
<span>{record.clm_total}</span>
<CurrencyFormatter>{record.clm_total}</CurrencyFormatter>
) : (
t("general.labels.unknown")
);

View File

@@ -4,5 +4,5 @@ import "./loading-skeleton.styles.scss";
import { Skeleton } from "antd";
export default function LoadingSkeleton(props) {
return <Skeleton {...props} className='loading-skeleton' active />;
return <Skeleton {...props} className="loading-skeleton" active />;
}

View File

@@ -1,15 +1,17 @@
import React from "react";
import { useTranslation } from "react-i18next";
import AlertComponent from "../alert/alert.component";
import ProfileMyComponent from "../profile-my/profile-my.component";
import ProfileShopsContainer from "../profile-shops/profile-shops.container";
export default function ProfileContent({ sidebarSelection }) {
const { t } = useTranslation();
switch (sidebarSelection.key) {
case "profile":
return <div>Profile stuff</div>;
case "shop":
return <div>Shop stuff</div>;
return <ProfileMyComponent />;
case "shops":
return <ProfileShopsContainer />;
default:
return (
<AlertComponent message={t("profile.errors.state")} type="error" />

View File

@@ -0,0 +1,74 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectCurrentUser } from "../../redux/user/user.selectors";
import AlertComponent from "../alert/alert.component";
import { Form, Input, notification, Button } from "antd";
import { updateUserDetails } from "../../redux/user/user.actions";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser
});
const mapDispatchToProps = dispatch => ({
updateUserDetails: userDetails => dispatch(updateUserDetails(userDetails))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(
Form.create({ name: "ProfileMyComponentForm" })(function ProfileMyComponent({
currentUser,
form,
updateUserDetails
}) {
const { isFieldsTouched, resetFields, getFieldDecorator } = form;
const { t } = useTranslation();
const handleSubmit = e => {
e.preventDefault();
form.validateFieldsAndScroll((err, values) => {
if (err) {
notification["error"]({
message: t("jobs.errors.validationtitle"),
description: t("jobs.errors.validation")
});
}
if (!err) {
console.log("values", values);
updateUserDetails({ displayName: values.displayname });
}
});
};
return (
<div>
{isFieldsTouched() ? (
//TODO: Appropriate Error
<AlertComponent
message={t("jobs.errors.validation")}
onClick={() => resetFields()}
/>
) : null}
<Form onSubmit={handleSubmit} autoComplete={"no"}>
<Form.Item label={t("user.fields.displayname")}>
{getFieldDecorator("displayname", {
initialValue: currentUser.displayName,
rules: [{ required: true }]
})(<Input name="displayname" />)}
</Form.Item>
<Button
type="primary"
key="submit"
htmlType="submit"
onClick={handleSubmit}
>
{t("user.actions.updateprofile")}
</Button>
</Form>
</div>
);
})
);

View File

@@ -0,0 +1,52 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { Table, Button } from "antd";
export default function ProfileShopsComponent({
loading,
data,
updateActiveShop
}) {
const { t } = useTranslation();
const columns = [
{
title: t("associations.fields.shopname"),
dataIndex: "shopname",
key: "shopname",
width: "25%",
render: (text, record) => <span>{record.bodyshop.shopname}</span>
},
{
title: t("associations.fields.active"),
dataIndex: "active",
key: "active",
width: "25%",
render: (text, record) => <span>{record.active ? "Yes" : "No"}</span>
},
{
title: t("associations.labels.actions"),
dataIndex: "actions",
key: "actions",
width: "25%",
render: (text, record) => (
<span>
{record.active ? null : (
<Button onClick={() => updateActiveShop(record.id)}>
Activate
</Button>
)}
</span>
)
}
];
return (
<Table
loading={loading}
size="small"
columns={columns.map(item => ({ ...item }))}
rowKey="id"
dataSource={data}
/>
);
}

View File

@@ -0,0 +1,34 @@
import React from "react";
import { useQuery, useMutation } from "react-apollo";
import {
QUERY_ALL_ASSOCIATIONS,
UPDATE_ASSOCIATION
} from "../../graphql/associations.queries";
import AlertComponent from "../alert/alert.component";
import ProfileShopsComponent from "./profile-shops.component";
export default function ProfileShopsContainer() {
const { loading, error, data, refetch } = useQuery(QUERY_ALL_ASSOCIATIONS);
const [updateAssocation] = useMutation(UPDATE_ASSOCIATION);
const updateActiveShop = activeShopId => {
data.associations.forEach(record => {
updateAssocation({
variables: {
assocId: record.id,
assocActive: record.id === activeShopId ? true : false
}
});
});
refetch();
};
if (error) return <AlertComponent type="error" message={error.message} />;
return (
<ProfileShopsComponent
loading={loading}
data={data ? data.associations : null}
updateActiveShop={updateActiveShop}
/>
);
}

View File

@@ -0,0 +1,33 @@
import moment from "moment";
import React from "react";
import { Calendar, momentLocalizer } from "react-big-calendar";
import "react-big-calendar/lib/css/react-big-calendar.css";
import DateCellWrapper from "../schedule-datecellwrapper/schedule-datecellwrapper.component";
import Event from "../schedule-event/schedule-event.container";
const localizer = momentLocalizer(moment);
export default function ScheduleCalendarWrapperComponent({
data,
refetch,
defaultView,
...otherProps
}) {
return (
<Calendar
events={data}
defaultView={defaultView}
step={30}
showMultiDayTimes
localizer={localizer}
min={new Date("2020-01-01T06:00:00")} //TODO: Read from business settings.
max={new Date("2020-01-01T20:00:00")}
components={{
event: e => {
return Event({ event: e.event, refetch: refetch });
},
dateCellWrapper: DateCellWrapper
}}
{...otherProps}
/>
);
}

View File

@@ -0,0 +1,13 @@
import React from "react";
//import "react-big-calendar/lib/css/react-big-calendar.css";
import ScheduleCalendarWrapperComponent from "../schedule-calendar-wrapper/scheduler-calendar-wrapper.component";
export default function ScheduleCalendarComponent({ data, refetch }) {
return (
<ScheduleCalendarWrapperComponent
data={data}
defaultView="week"
refetch={refetch}
/>
);
}

View File

@@ -0,0 +1,34 @@
import React from "react";
import { useQuery } from "react-apollo";
import ScheduleCalendarComponent from "./schedule-calendar.component";
import { QUERY_ALL_ACTIVE_APPOINTMENTS } from "../../graphql/appointments.queries";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import AlertComponent from "../alert/alert.component";
export default function ScheduleCalendarContainer() {
const { loading, error, data, refetch } = useQuery(
QUERY_ALL_ACTIVE_APPOINTMENTS,
{
fetchPolicy: "network-only"
}
);
if (loading) return <LoadingSpinner />;
if (error) return <AlertComponent message={error.message} type="error" />;
let normalizedData = data.appointments.map(e => {
//Required becuase Hasura returns a string instead of a date object.
return Object.assign(
{},
e,
{ start: new Date(e.start) },
{ end: new Date(e.end) }
);
});
return (
<ScheduleCalendarComponent
refetch={refetch}
data={data ? normalizedData : null}
/>
);
}

View File

@@ -0,0 +1,18 @@
import React from "react";
export default function ScheduleDateCellWrapper(dateCellWrapperProps) {
// Show 'click me' text in arbitrary places by using the range prop
const style = {
display: "flex",
flex: 1,
borderLeft: "1px solid #DDD",
backgroundColor: "#fff"
};
return (
<div style={style}>
PLACEHOLDER:DATA
{dateCellWrapperProps.children}
</div>
);
}

View File

@@ -0,0 +1,20 @@
import React from "react";
import "react-big-calendar/lib/css/react-big-calendar.css";
import { useTranslation } from "react-i18next";
import ScheduleCalendarWrapperComponent from "../schedule-calendar-wrapper/scheduler-calendar-wrapper.component";
export default function ScheduleDayViewComponent({ data, day }) {
const { t } = useTranslation();
if (data)
//TODO Remove addtional calendar elements from day view.
return (
<ScheduleCalendarWrapperComponent
events={data}
defaultView="day"
style={{ height: "40vh" }}
defaultDate={new Date(day)}
//onNavigate={e => console.log("e", e)}
/>
);
else return <div>{t("appointments.labels.nodateselected")}</div>;
}

View File

@@ -0,0 +1,36 @@
import React from "react";
import ScheduleDayViewComponent from "./schedule-day-view.component";
import { useQuery } from "react-apollo";
import { QUERY_APPOINTMENT_BY_DATE } from "../../graphql/appointments.queries";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import moment from "moment";
export default function ScheduleDayViewContainer({ day }) {
const { loading, error, data } = useQuery(QUERY_APPOINTMENT_BY_DATE, {
variables: {
start: moment(day).startOf("day"),
end: moment(day).endOf("day")
},
skip: !day,
fetchPolicy: "network-only"
});
if (loading) return <LoadingSkeleton paragraph={{ rows: 4 }} />;
if (error) return <div>{error.message}</div>;
let normalizedData;
if (data) {
normalizedData = data.appointments.map(e => {
//Required becuase Hasura returns a string instead of a date object.
return Object.assign(
{},
e,
{ start: new Date(e.start) },
{ end: new Date(e.end) }
);
});
}
return (
<ScheduleDayViewComponent data={data ? normalizedData : null} day={day} />
);
}

View File

@@ -0,0 +1,58 @@
import React from "react";
import { Popover, Button } from "antd";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
import PhoneFormatter from "../../utils/PhoneFormatter";
import { Link } from "react-router-dom";
import { useTranslation } from "react-i18next";
export default function ScheduleEventComponent({ event, handleCancel }) {
const { t } = useTranslation();
const popoverContent = (
<div>
<div>{`${t("jobs.fields.ro_number")}: ${event.job.ro_number}`}</div>
<div>
{t("jobs.fields.clm_total")}:
<CurrencyFormatter>{event.job.clm_total}</CurrencyFormatter>
</div>
<div>{`${t("jobs.fields.clm_no")}: ${event.job.clm_no}`}</div>
<div>
{t("jobs.fields.ownr_ea")}:{event.job.ownr_ea}
</div>
<div>
{t("jobs.fields.ownr_ph1")}:
<PhoneFormatter>{event.job.ownr_ph1}</PhoneFormatter>
</div>
<Link to={`/manage/jobs/${event.job.id}`}>
<Button>{t("appointments.actions.viewjob")}</Button>
</Link>
<Button onClick={() => handleCancel(event.id)}>
{t("appointments.actions.cancel")}
</Button>
<Button>
{
//TODO: Add reschedule Func.
}
{t("appointments.actions.reschedule")}
</Button>
<Button>
{
//TODO: Add intake func.
}
{t("appointments.actions.intake")}
</Button>
</div>
);
return (
<Popover content={popoverContent}>
<div>
<strong>{`${event.job.ownr_fn || ""} ${event.job.ownr_ln ||
""}`}</strong>
<span style={{ margin: 4 }}>
{`${event.job.vehicle.v_model_yr || ""} ${event.job.vehicle
.v_make_desc || ""} ${event.job.vehicle.v_model_desc || ""}`}
</span>
</div>
</Popover>
);
}

View File

@@ -0,0 +1,24 @@
import React from "react";
import { useMutation } from "react-apollo";
import { CANCEL_APPOINTMENT_BY_ID } from "../../graphql/appointments.queries";
import ScheduleEventComponent from "./schedule-event.component";
import { notification } from "antd";
import { useTranslation } from "react-i18next";
export default function ScheduleEventContainer({ event, refetch }) {
const { t } = useTranslation();
const [cancelAppointment] = useMutation(CANCEL_APPOINTMENT_BY_ID);
const handleCancel = id => {
cancelAppointment({ variables: { appid: event.id } })
.then(r => {
notification["success"]({
message: t("appointments.successes.canceled")
});
if (refetch) refetch();
})
.catch(error => {
notification["error"]({ message: t("appointments.errors.canceling") });
});
};
return <ScheduleEventComponent event={event} handleCancel={handleCancel} />;
}

View File

@@ -0,0 +1,44 @@
import React from "react";
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
import AlertComponent from "../alert/alert.component";
import { Timeline } from "antd";
import { useTranslation } from "react-i18next";
import { DateTimeFormatter } from "../../utils/DateFormatter";
export default function ScheduleExistingAppointmentsList({
existingAppointments
}) {
const { t } = useTranslation();
if (existingAppointments.loading) return <LoadingSpinner />;
if (existingAppointments.error)
return (
<AlertComponent
message={existingAppointments.error.message}
type="error"
/>
);
return (
<div>
{t("appointments.labels.priorappointments")}
<Timeline>
{existingAppointments.data.appointments.map(item => {
return (
<Timeline.Item
key={item.id}
color={item.canceled ? "red" : item.arrived ? "green" : "blue"}
>
{item.canceled
? t("appointments.labels.cancelledappointment")
: item.arrived
? t("appointments.labels.arrivedon")
: t("appointments.labels.scheduledfor")}
<DateTimeFormatter>{item.start}</DateTimeFormatter>
</Timeline.Item>
);
})}
</Timeline>
</div>
);
}

View File

@@ -0,0 +1,72 @@
import { Checkbox, Col, DatePicker, Modal, Row, Tabs, TimePicker } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import ScheduleDayViewContainer from "../schedule-day-view/schedule-day-view.container";
import ScheduleExistingAppointmentsList from "../schedule-existing-appointments-list/schedule-existing-appointments-list.component";
export default function ScheduleJobModalComponent({
existingAppointments,
appData,
setAppData,
formData,
setFormData,
...props
}) {
const { t } = useTranslation();
//TODO: Existing appointments list only refreshes sometimes after modal close. May have to do with the container class.
return (
<Modal
{...props}
width={"80%"}
maskClosable={false}
destroyOnClose={true}
okButtonProps={{ disabled: appData.start ? false : true }}
>
<Row>
<Col span={14}>
<Tabs defaultActiveKey="1">
<Tabs.TabPane tab="SMART Scheduling" key="auto">
Automatic Job Selection.
</Tabs.TabPane>
<Tabs.TabPane tab="Manual Scheduling" key="manual">
<Row>
Manual Job Selection Scheduled Time
<DatePicker
value={appData.start}
onChange={e => {
setAppData({ ...appData, start: e });
}}
/>
<TimePicker
value={appData.start}
format={"HH:mm"}
minuteStep={15}
onChange={e => {
setAppData({ ...appData, start: e });
}}
/>
</Row>
</Tabs.TabPane>
</Tabs>
<ScheduleExistingAppointmentsList
existingAppointments={existingAppointments}
/>
{
//TODO: Build out notifications.
}
<Checkbox
defaultChecked={formData.notifyCustomer}
onChange={e =>
setFormData({ ...formData, notifyCustomer: e.target.checked })
}
>
{t("jobs.labels.appointmentconfirmation")}
</Checkbox>
</Col>
<Col span={10}>
<ScheduleDayViewContainer day={appData.start} />
</Col>
</Row>
</Modal>
);
}

View File

@@ -0,0 +1,88 @@
import React, { useState } from "react";
import ScheduleJobModalComponent from "./schedule-job-modal.component";
import { useMutation, useQuery } from "react-apollo";
import {
INSERT_APPOINTMENT,
QUERY_APPOINTMENTS_BY_JOBID
} from "../../graphql/appointments.queries";
import moment from "moment";
import { notification } from "antd";
import { useTranslation } from "react-i18next";
import { UPDATE_JOB_STATUS } from "../../graphql/jobs.queries";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop
});
export default connect(
mapStateToProps,
null
)(function ScheduleJobModalContainer({
scheduleModalState,
jobId,
bodyshop,
refetch
}) {
const existingAppointments = useQuery(QUERY_APPOINTMENTS_BY_JOBID, {
variables: { jobid: jobId },
fetchPolicy: "network-only",
skip: !jobId
});
const [scheduleModalVisible, setscheduleModalVisible] = scheduleModalState;
const [appData, setAppData] = useState({ jobid: jobId, start: null });
const [insertAppointment] = useMutation(INSERT_APPOINTMENT);
const [updateJobStatus] = useMutation(UPDATE_JOB_STATUS, {
variables: {
jobId: jobId,
status: bodyshop.md_ro_statuses.default_scheduled
}
});
const [formData, setFormData] = useState({ notifyCustomer: false });
const { t } = useTranslation();
return (
<ScheduleJobModalComponent
existingAppointments={existingAppointments}
appData={appData}
setAppData={setAppData}
formData={formData}
setFormData={setFormData}
//Spreadable Modal Props
visible={scheduleModalVisible}
onCancel={() => setscheduleModalVisible(false)}
onOk={() => {
//TODO: Customize the amount of minutes it will add.
insertAppointment({
variables: {
app: { ...appData, end: moment(appData.start).add(60, "minutes") }
}
})
.then(r => {
updateJobStatus().then(r => {
notification["success"]({
message: t("appointments.successes.created")
});
if (formData.notifyCustomer) {
//TODO: Implement customer reminder on scheduling.
alert("Chosed to notify the customer somehow!");
}
setscheduleModalVisible(false);
if (refetch) refetch();
});
})
.catch(error => {
notification["error"]({
message: t("appointments.errors.saving", {
message: error.message
})
});
});
}}
/>
);
});

View File

@@ -34,6 +34,7 @@ export const createUserProfileDocument = async (userAuth, additionalData) => {
};
export const auth = firebase.auth();
export const firestore = firebase.firestore();
const provider = new firebase.auth.GoogleAuthProvider();
@@ -49,3 +50,15 @@ export const getCurrentUser = () => {
}, reject);
});
};
export const updateCurrentUser = userDetails => {
return new Promise((resolve, reject) => {
const unsubscribe = auth.onAuthStateChanged(userAuth => {
console.log("userDetails", userDetails);
userAuth.updateProfile(userDetails).then(r => {
unsubscribe();
resolve(userAuth);
});
}, reject);
});
};

View File

@@ -42,8 +42,9 @@ const errorLink = onError(
authorization: token ? `Bearer ${token}` : ""
}
});
return forward(operation);
console.log("forward", forward);
console.log("operation", operation);
return forward(operation).subscribe();
// return new Observable(observer => {
// const subscriber = {

View File

@@ -0,0 +1,89 @@
import { gql } from "apollo-boost";
export const QUERY_ALL_ACTIVE_APPOINTMENTS = gql`
query QUERY_ALL_ACTIVE_APPOINTMENTS {
appointments(where: { canceled: { _eq: false } }) {
start
id
end
job {
ro_number
ownr_ln
ownr_fn
ownr_ph1
ownr_ea
clm_total
id
clm_no
vehicle {
id
v_model_yr
v_make_desc
v_model_desc
}
}
}
}
`;
export const INSERT_APPOINTMENT = gql`
mutation INSERT_APPOINTMENT($app: [appointments_insert_input!]!) {
insert_appointments(objects: $app) {
returning {
id
}
}
}
`;
export const QUERY_APPOINTMENT_BY_DATE = gql`
query QUERY_APPOINTMENT_BY_DATE($start: timestamptz, $end: timestamptz) {
appointments(where: { start: { _lte: $end, _gte: $start } }) {
start
id
end
job {
ro_number
ownr_ln
ownr_fn
ownr_ph1
ownr_ea
clm_total
id
clm_no
vehicle {
id
v_model_yr
v_make_desc
v_model_desc
}
}
}
}
`;
export const CANCEL_APPOINTMENT_BY_ID = gql`
mutation CANCEL_APPOINTMENT_BY_ID($appid: uuid!) {
update_appointments(
where: { id: { _eq: $appid } }
_set: { canceled: true }
) {
returning {
id
}
}
}
`;
export const QUERY_APPOINTMENTS_BY_JOBID = gql`
query QUERY_APPOINTMENTS_BY_JOBID($jobid: uuid!) {
appointments(where: { jobid: { _eq: $jobid } }) {
start
id
end
arrived
canceled
created_at
}
}
`;

View File

@@ -0,0 +1,27 @@
import { gql } from "apollo-boost";
export const QUERY_ALL_ASSOCIATIONS = gql`
query QUERY_ALL_ASSOCIATIONS {
associations {
id
active
bodyshop {
shopname
}
}
}
`;
export const UPDATE_ASSOCIATION = gql`
mutation UPDATE_ASSOCIATION($assocId: uuid, $assocActive: Boolean) {
update_associations(
where: { id: { _eq: $assocId } }
_set: { active: $assocActive }
) {
returning {
id
active
}
}
}
`;

View File

@@ -19,6 +19,7 @@ export const QUERY_BODYSHOP = gql`
state_tax_id
updated_at
zip_post
region_config
}
}
`;

View File

@@ -42,12 +42,9 @@ export const QUERY_ALL_OPEN_JOBS = gql`
scheduled_completion
scheduled_in
scheduled_delivery
job_status {
id
name
}
status
updated_at
claim_total
clm_total
ded_amt
vehicle {
id
@@ -195,7 +192,7 @@ export const GET_JOB_BY_PK = gql`
date_invoiced
date_closed
date_exported
status
joblines {
id
unq_seq
@@ -213,6 +210,11 @@ export const GET_JOB_BY_PK = gql`
lbr_amt
op_code_desc
}
appointments_aggregate {
aggregate {
count
}
}
}
}
`;
@@ -253,6 +255,7 @@ export const QUERY_JOB_CARD_DETAILS = gql`
est_ct_fn
est_ct_ln
clm_no
status
ro_number
scheduled_completion
scheduled_in
@@ -264,12 +267,9 @@ export const QUERY_JOB_CARD_DETAILS = gql`
private
created_at
}
job_status {
id
name
}
updated_at
claim_total
clm_total
ded_amt
documents(limit: 3, order_by: { created_at: desc }) {
id
@@ -321,3 +321,13 @@ export const INSERT_NEW_JOB = gql`
}
}
`;
export const UPDATE_JOB_STATUS = gql`
mutation UPDATE_JOB_STATUS($jobId: uuid!, $status: String!) {
update_jobs(where: { id: { _eq: $jobId } }, _set: { status: $status }) {
returning {
id
}
}
}
`;

View File

@@ -16,13 +16,16 @@ import JobsDocumentsContainer from "../../components/jobs-documents/jobs-documen
import JobNotesContainer from "../../components/jobs-notes/jobs-notes.container";
import JobDetailFormContext from "./jobs-detail.page.context";
import JobsDetailDatesComponent from "../../components/jobs-detail-dates/jobs-detail-dates.component";
import ScheduleJobModalContainer from "../../components/schedule-job-modal/schedule-job-modal.container";
export default function JobsDetailPage({
job,
mutationUpdateJob,
mutationConvertJob,
handleSubmit,
refetch
refetch,
scheduleModalState,
updateJobStatus
}) {
const { t } = useTranslation();
@@ -41,12 +44,20 @@ export default function JobsDetailPage({
return (
<div>
<ScheduleJobModalContainer
scheduleModalState={scheduleModalState}
jobId={job.id}
refetch={refetch}
/>
<Form onSubmit={handleSubmit} {...formItemLayout} autoComplete={"off"}>
<JobsDetailHeader
job={job}
mutationConvertJob={mutationConvertJob}
refetch={refetch}
handleSubmit={handleSubmit}
scheduleModalState={scheduleModalState}
updateJobStatus={updateJobStatus}
/>
{isFieldsTouched() ? (

View File

@@ -1,5 +1,5 @@
import { Form, notification } from "antd";
import React, { useEffect } from "react";
import React, { useEffect, useState } from "react";
import { useMutation, useQuery } from "react-apollo";
import { useTranslation } from "react-i18next";
import AlertComponent from "../../components/alert/alert.component";
@@ -7,7 +7,8 @@ import SpinComponent from "../../components/loading-spinner/loading-spinner.comp
import {
CONVERT_JOB_TO_RO,
GET_JOB_BY_PK,
UPDATE_JOB
UPDATE_JOB,
UPDATE_JOB_STATUS
} from "../../graphql/jobs.queries";
import JobsDetailPage from "./jobs-detail.page.component";
import JobDetailFormContext from "./jobs-detail.page.context";
@@ -16,12 +17,29 @@ function JobsDetailPageContainer({ match, form }) {
const { jobId } = match.params;
const { t } = useTranslation();
const scheduleModalState = useState(false);
const { loading, error, data, refetch } = useQuery(GET_JOB_BY_PK, {
variables: { id: jobId },
fetchPolicy: "network-only"
});
const [mutationUpdateJob] = useMutation(UPDATE_JOB);
const [mutationConvertJob] = useMutation(CONVERT_JOB_TO_RO);
const [mutationUpdateJobstatus] = useMutation(UPDATE_JOB_STATUS);
const updateJobStatus = status => {
mutationUpdateJobstatus({
variables: { jobId: jobId, status: status }
})
.then(r => {
notification["success"]({ message: t("jobs.successes.save") });
refetch();
})
.catch(error => {
//TODO Error handling.
console.log("error", error);
});
};
useEffect(() => {
document.title = loading
@@ -71,6 +89,8 @@ function JobsDetailPageContainer({ match, form }) {
handleSubmit={handleSubmit}
getFieldDecorator={form.getFieldDecorator}
refetch={refetch}
scheduleModalState={scheduleModalState}
updateJobStatus={updateJobStatus}
/>
</JobDetailFormContext.Provider>
) : (

View File

@@ -1,16 +1,21 @@
import { BackTop, Layout } from "antd";
import { BackTop, Layout, notification } from "antd";
import React, { lazy, Suspense, useEffect } from "react";
import { useTranslation } from "react-i18next";
//This page will handle all routing for the entire application.
import { connect } from "react-redux";
import { Route } from "react-router";
import ChatWindowContainer from "../../components/chat-window/chat-window.container";
import { createStructuredSelector } from "reselect";
import { useQuery } from "react-apollo";
import { QUERY_BODYSHOP } from "../../graphql/bodyshop.queries";
import ErrorBoundary from "../../components/error-boundary/error-boundary.component";
import FooterComponent from "../../components/footer/footer.component";
//Component Imports
import HeaderContainer from "../../components/header/header.container";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
//const WhiteBoardPage = lazy(() => import("../white-board/white-board.page"));
import { setBodyshop } from "../../redux/user/user.actions";
import "./manage.page.styles.scss";
//const WhiteBoardPage = lazy(() => import("../white-board/white-board.page"));
const ManageRootPage = lazy(() =>
import("../manage-root/manage-root.page.container")
);
@@ -25,12 +30,35 @@ const JobsDocumentsPage = lazy(() =>
const JobsAvailablePage = lazy(() =>
import("../jobs-available/jobs-available.page.container")
);
const ChatWindowContainer = lazy(() =>
import("../../components/chat-window/chat-window.container")
);
const ScheduleContainer = lazy(() =>
import("../schedule/schedule.page.container")
);
const { Header, Content, Footer, Sider } = Layout;
//This page will handle all routing for the entire application.
export default function Manage({ match }) {
const { Header, Content, Footer } = Layout;
const mapDispatchToProps = dispatch => ({
setBodyshop: bs => dispatch(setBodyshop(bs))
});
export default connect(
null,
mapDispatchToProps
)(function Manage({ match, setBodyshop }) {
const { t } = useTranslation();
const { error, data } = useQuery(QUERY_BODYSHOP, {
fetchPolicy: "network-only"
});
if (error) {
notification["error"]({ message: t("bodyshop.errors.loading") });
}
if (data) {
setBodyshop(data.bodyshops[0]);
}
useEffect(() => {
document.title = t("titles.app");
}, [t]);
@@ -41,17 +69,9 @@ export default function Manage({ match }) {
<HeaderContainer />
</Header>
<Layout>
<Sider
collapsible
defaultCollapsed
theme="light"
collapsedWidth={0}
width={300}
>
<chatWindowContainer />
</Sider>
<ChatWindowContainer />
<Content className="content-container" style={{ margin: "50px" }}>
<Content className="content-container" style={{ margin: "0px" }}>
<ErrorBoundary>
<Suspense
fallback={
@@ -78,6 +98,12 @@ export default function Manage({ match }) {
component={ProfilePage}
/>
<Route
exact
path={`${match.path}/schedule`}
component={ScheduleContainer}
/>
<Route
exact
path={`${match.path}/available`}
@@ -93,4 +119,4 @@ export default function Manage({ match }) {
<BackTop />
</Layout>
);
}
});

View File

@@ -0,0 +1,6 @@
import React from "react";
import ScheduleCalendarContainer from "../../components/schedule-calendar/schedule-calendar.container";
export default function SchedulePageComponent() {
return <ScheduleCalendarContainer />;
}

View File

@@ -0,0 +1,12 @@
import React, { useEffect } from "react";
import SchedulePageComponent from "./schedule.page.component";
import { useTranslation } from "react-i18next";
export default function SchedulePageContainer() {
const { t } = useTranslation();
useEffect(() => {
document.title = t("titles.schedule");
}, [t]);
return <SchedulePageComponent />;
}

View File

@@ -0,0 +1,7 @@
import MessagingActionTypes from './messaging.types'
export const toggleChatVisible = () => ({
type: MessagingActionTypes.TOGGLE_CHAT_VISIBLE,
//payload: user
});

View File

@@ -0,0 +1,24 @@
import MessagingActionTypes from "./messaging.types";
const INITIAL_STATE = {
visible: false
};
const messagingReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case MessagingActionTypes.TOGGLE_CHAT_VISIBLE:
return {
...state,
visible: !state.visible
};
case MessagingActionTypes.SET_CHAT_VISIBLE:
return {
...state,
visible: true
};
default:
return state;
}
};
export default messagingReducer;

View File

@@ -0,0 +1,90 @@
import {
all
// call, put, takeLatest
} from "redux-saga/effects";
//import { auth, getCurrentUser } from "../../firebase/firebase.utils";
// import { toggleChatVisible } from "./messaging.actions";
// import MessagingActionTypes from "./messaging.types";
// export function* getSnapshotFromUserAuth(userAuth) {
// try {
// const userRef = yield call(createUserProfileDocument, userAuth);
// //const userSnapshot = yield userRef.get();
// } catch (error) {
// yield put(signInFailure(error));
// }
// }
// export function* signInWithEmail({ payload: { email, password } }) {
// try {
// const { user } = yield auth.signInWithEmailAndPassword(email, password);
// yield put(
// signInSuccess({
// uid: user.uid,
// email: user.email,
// displayName: user.displayName,
// authorized: true
// })
// );
// } catch (error) {
// yield put(signInFailure(error));
// }
// }
// //This is the listener fo rthe call, and when it finds it, it triggers somethign else.
// export function* onEmailSignInStart() {
// yield takeLatest(UserActionTypes.EMAIL_SIGN_IN_START, signInWithEmail);
// }
// export function* isUserAuthenticated() {
// try {
// const user = yield getCurrentUser();
// if (!user) {
// yield put(unauthorizedUser());
// return;
// }
// let token = yield user.getIdToken();
// localStorage.setItem("token", token);
// window.sessionStorage.setItem(`lastTokenRefreshTime`, new Date());
// yield put(
// signInSuccess({
// uid: user.uid,
// email: user.email,
// displayName: user.displayName,
// authorized: true
// })
// );
// } catch (error) {
// yield put(signInFailure(error));
// }
// }
// export function* onCheckUserSession() {
// yield takeLatest(UserActionTypes.CHECK_USER_SESSION, isUserAuthenticated);
// }
// export function* signOutStart() {
// try {
// yield auth.signOut();
// yield put(signOutSuccess());
// localStorage.removeItem("token");
// } catch (error) {
// yield put(signOutFailure(error.message));
// }
// }
// export function* onSignOutStart() {
// yield takeLatest(UserActionTypes.SIGN_OUT_START, signOutStart);
// }
export function* messagingSagas() {
yield all([
// call(onGoogleSignInStart),
// call(onEmailSignInStart),
// call(onCheckUserSession),
// call(onSignOutStart)
// call(onEmailSignUpStart),
// call(onEmailSignUpSuccess)
]);
}

View File

@@ -0,0 +1,8 @@
import { createSelector } from "reselect";
const selectMessaging = state => state.messaging;
export const selectChatVisible = createSelector(
[selectMessaging],
messaging => messaging.visible
);

View File

@@ -0,0 +1,5 @@
const MessagingActionTypes = {
TOGGLE_CHAT_VISIBLE: "TOGGLE_CHAT_VISIBLE",
SET_CHAT_VISIBLE: "SET_CHAT_VISIBLE"
};
export default MessagingActionTypes;

View File

@@ -3,6 +3,7 @@ import { persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
import userReducer from "./user/user.reducer";
import messagingReducer from "./messaging/messaging.reducer";
// import cartReducer from './cart/cart.reducer';
// import directoryReducer from './directory/directory.reducer';
// import shopReducer from './shop/shop.reducer';
@@ -15,7 +16,8 @@ const persistConfig = {
};
const rootReducer = combineReducers({
user: userReducer
user: userReducer,
messaging: messagingReducer
// cart: cartReducer,
// directory: directoryReducer,
// shop: shopReducer

View File

@@ -3,9 +3,10 @@ import { all, call } from "redux-saga/effects";
//List of all Sagas
// import { shopSagas } from "./shop/shop.sagas";
import { userSagas } from "./user/user.sagas";
import { messagingSagas } from "./messaging/messaging.sagas";
//import { cartSagas } from "./cart/cart.sagas";
export default function* rootSaga() {
//All starts all the Sagas concurrently.
yield all([call(userSagas)]);
yield all([call(userSagas), call(messagingSagas)]);
}

View File

@@ -38,3 +38,18 @@ export const setUserLanguage = language => ({
type: UserActionTypes.SET_USER_LANGUAGE,
payload: language
});
export const updateUserDetails = userDetails => ({
type: UserActionTypes.UPDATE_USER_DETAILS,
payload: userDetails
});
export const updateUserDetailsSuccess = userDetails => ({
type: UserActionTypes.UPDATE_USER_DETAILS_SUCCESS,
payload: userDetails
});
export const setBodyshop = bodyshop => ({
type: UserActionTypes.SET_SHOP_DETAILS,
payload: bodyshop
});

View File

@@ -3,8 +3,9 @@ import UserActionTypes from "./user.types";
const INITIAL_STATE = {
currentUser: {
authorized: null,
language: "en_US"
language: "en_us"
},
bodyshop: null,
error: null
};
@@ -33,10 +34,20 @@ const userReducer = (state = INITIAL_STATE, action) => {
...state,
language: action.payload
};
case UserActionTypes.UPDATE_USER_DETAILS_SUCCESS:
return {
...state,
currentUser: {
...state.currentUser,
...action.payload //Spread current user details in.
}
};
case UserActionTypes.SET_SHOP_DETAILS:
return { ...state, bodyshop: action.payload };
case UserActionTypes.SIGN_IN_FAILURE:
case UserActionTypes.SIGN_OUT_FAILURE:
case UserActionTypes.EMAIL_SIGN_UP_FAILURE:
console.log("Reduced getting called.");
return {
...state,
error: action.payload

View File

@@ -1,6 +1,17 @@
import { all, call, put, takeLatest } from "redux-saga/effects";
import { auth, getCurrentUser } from "../../firebase/firebase.utils";
import { signInFailure, signInSuccess, signOutFailure, signOutSuccess, unauthorizedUser } from "./user.actions";
import {
auth,
getCurrentUser,
updateCurrentUser
} from "../../firebase/firebase.utils";
import {
signInFailure,
signInSuccess,
signOutFailure,
signOutSuccess,
unauthorizedUser,
updateUserDetailsSuccess
} from "./user.actions";
import UserActionTypes from "./user.types";
// export function* getSnapshotFromUserAuth(userAuth) {
@@ -15,6 +26,9 @@ import UserActionTypes from "./user.types";
export function* signInWithEmail({ payload: { email, password } }) {
try {
const { user } = yield auth.signInWithEmailAndPassword(email, password);
let token = yield user.getIdToken();
localStorage.setItem("token", token);
window.sessionStorage.setItem(`lastTokenRefreshTime`, new Date());
yield put(
signInSuccess({
uid: user.uid,
@@ -74,14 +88,24 @@ export function* onSignOutStart() {
yield takeLatest(UserActionTypes.SIGN_OUT_START, signOutStart);
}
export function* onUpdateUserDetails() {
yield takeLatest(UserActionTypes.UPDATE_USER_DETAILS, updateUserDetails);
}
export function* updateUserDetails(userDetails) {
try {
yield updateCurrentUser(userDetails.payload);
yield put(updateUserDetailsSuccess(userDetails.payload));
} catch (error) {
//yield put(signOutFailure(error.message));
//TODO: error handling
}
}
export function* userSagas() {
yield all([
// call(onGoogleSignInStart),
call(onEmailSignInStart),
call(onCheckUserSession),
call(onSignOutStart),
// call(onEmailSignUpStart),
// call(onEmailSignUpSuccess)
call(onUpdateUserDetails)
]);
}

View File

@@ -11,3 +11,8 @@ export const selectSignInError = createSelector(
[selectUser],
user => user.error
);
export const selectBodyshop = createSelector(
[selectUser],
user => user.bodyshop
);

View File

@@ -12,6 +12,9 @@ const UserActionTypes = {
EMAIL_SIGN_UP_SUCCESS: "EMAIL_SIGN_UP_SUCCESS",
EMAIL_SIGN_UP_FAILURE: "EMAIL_SIGN_UP_FAILURE",
UNAUTHORIZED_USER: "UNAUTHORIZED_USER",
SET_USER_LANGUAGE: "SET_USER_LANGUAGE"
SET_USER_LANGUAGE: "SET_USER_LANGUAGE",
UPDATE_USER_DETAILS: "UPDATE_USER_DETAILS",
UPDATE_USER_DETAILS_SUCCESS: "UPDATE_USER_DETAILS_SUCCESS",
SET_SHOP_DETAILS: "SET_SHOP_DETAILS"
};
export default UserActionTypes;

View File

@@ -1,5 +1,45 @@
{
"translation": {
"appointments": {
"actions": {
"cancel": "Cancel",
"intake": "Intake",
"reschedule": "Reschedule",
"viewjob": "View Job"
},
"errors": {
"canceling": "Error canceling appointment.",
"saving": "Error scheduling appointment. {{message}}"
},
"labels": {
"arrivedon": "Arrived on: ",
"cancelledappointment": "Canceled appointment for: ",
"nodateselected": "No date has been selected.",
"priorappointments": "Previous Appointments",
"scheduledfor": "Scheduled appointment for: "
},
"successes": {
"canceled": "Appointment canceled successfully.",
"created": "Appointment scheduled successfully."
}
},
"associations": {
"actions": {
"activate": "Activate"
},
"fields": {
"active": "Active?",
"shopname": "Shop Name"
},
"labels": {
"actions": "Actions"
}
},
"bodyshop": {
"errors": {
"loading": "Unable to load shop details. Please call technical support."
}
},
"documents": {
"errors": {
"deletes3": "Error deleting document from storage. ",
@@ -52,9 +92,11 @@
"actions": {
"addDocuments": "Add Job Documents",
"addNote": "Add Note",
"changestatus": "Change Status",
"convert": "Convert",
"postInvoices": "Post Invoices",
"printCenter": "Print Center"
"printCenter": "Print Center",
"schedule": "Schedule"
},
"errors": {
"creating": "Error encountered while creating job. {{error}}",
@@ -113,8 +155,8 @@
"owner": "Owner",
"owner_owing": "Cust. Owes",
"ownr_ea": "Email",
"ownr_ph1": "Phone 1",
"pay_date": "Inspection Date",
"phone1": "Phone 1",
"phoneshort": "PH",
"policy_no": "Policy #",
"ponumber": "PO Number",
@@ -163,6 +205,7 @@
"vehicle": "Vehicle"
},
"labels": {
"appointmentconfirmation": "Send confirmation to customer?",
"available_new_jobs": "",
"cards": {
"appraiser": "Appraiser",
@@ -191,7 +234,7 @@
"converted": "Job converted successfully.",
"created": "Job created successfully. Click to view.",
"deleted": "Job deleted successfully.",
"save": "Record Saved",
"save": "Job saved successfully.",
"savetitle": "Record saved successfully."
}
},
@@ -204,7 +247,8 @@
"activejobs": "Active Jobs",
"availablejobs": "Available Jobs",
"home": "Home",
"jobs": "Jobs"
"jobs": "Jobs",
"schedule": "Schedule"
},
"jobsdetail": {
"claimdetail": "Claim Details",
@@ -267,11 +311,16 @@
"jobsavailable": "Available Jobs | $t(titles.app)",
"jobsdetail": "Job {{ro_number}} | $t(titles.app)",
"jobsdocuments": "Job Documents {{ro_number}} | $t(titles.app)",
"profile": "My Profile | $t(titles.app)"
"profile": "My Profile | $t(titles.app)",
"schedule": "Schedule | $t(titles.app)"
},
"user": {
"actions": {
"signout": "Sign Out"
"signout": "Sign Out",
"updateprofile": "Update Profile"
},
"fields": {
"displayname": "Display Name"
}
},
"vehicles": {

View File

@@ -1,5 +1,45 @@
{
"translation": {
"appointments": {
"actions": {
"cancel": "Cancelar",
"intake": "Consumo",
"reschedule": "Reprogramar",
"viewjob": "Ver trabajo"
},
"errors": {
"canceling": "Error al cancelar la cita.",
"saving": "Error al programar la cita. {{message}}"
},
"labels": {
"arrivedon": "Llegado el:",
"cancelledappointment": "Cita cancelada para:",
"nodateselected": "No se ha seleccionado ninguna fecha.",
"priorappointments": "Nombramientos previos",
"scheduledfor": "Cita programada para:"
},
"successes": {
"canceled": "Cita cancelada con éxito.",
"created": "Cita programada con éxito."
}
},
"associations": {
"actions": {
"activate": "Activar"
},
"fields": {
"active": "¿Activo?",
"shopname": "Nombre de tienda"
},
"labels": {
"actions": "Comportamiento"
}
},
"bodyshop": {
"errors": {
"loading": "No se pueden cargar los detalles de la tienda. Por favor llame al soporte técnico."
}
},
"documents": {
"errors": {
"deletes3": "Error al eliminar el documento del almacenamiento.",
@@ -52,9 +92,11 @@
"actions": {
"addDocuments": "Agregar documentos de trabajo",
"addNote": "Añadir la nota",
"changestatus": "Cambiar Estado",
"convert": "Convertir",
"postInvoices": "Contabilizar facturas",
"printCenter": "Centro de impresión"
"printCenter": "Centro de impresión",
"schedule": "Programar"
},
"errors": {
"creating": "",
@@ -113,8 +155,8 @@
"owner": "Propietario",
"owner_owing": "Cust. Debe",
"ownr_ea": "Email",
"ownr_ph1": "Teléfono 1",
"pay_date": "Fecha de inspección",
"phone1": "Teléfono 1",
"phoneshort": "PH",
"policy_no": "Política #",
"ponumber": "numero postal",
@@ -163,6 +205,7 @@
"vehicle": "Vehículo"
},
"labels": {
"appointmentconfirmation": "¿Enviar confirmación al cliente?",
"available_new_jobs": "",
"cards": {
"appraiser": "Tasador",
@@ -191,7 +234,7 @@
"converted": "Trabajo convertido con éxito.",
"created": "Trabajo creado con éxito. Click para ver.",
"deleted": "Trabajo eliminado con éxito.",
"save": "Registro guardado",
"save": "Trabajo guardado con éxito.",
"savetitle": "Registro guardado con éxito."
}
},
@@ -204,7 +247,8 @@
"activejobs": "Empleos activos",
"availablejobs": "Trabajos disponibles",
"home": "Casa",
"jobs": "Trabajos"
"jobs": "Trabajos",
"schedule": "Programar"
},
"jobsdetail": {
"claimdetail": "Detalles de la reclamación",
@@ -267,11 +311,16 @@
"jobsavailable": "Empleos disponibles | $t(titles.app)",
"jobsdetail": "Trabajo {{ro_number}} | $t(titles.app)",
"jobsdocuments": "Documentos de trabajo {{ro_number}} | $ t (títulos.app)",
"profile": "Mi perfil | $t(titles.app)"
"profile": "Mi perfil | $t(titles.app)",
"schedule": "Horario | $t(titles.app)"
},
"user": {
"actions": {
"signout": "desconectar"
"signout": "desconectar",
"updateprofile": "Actualización del perfil"
},
"fields": {
"displayname": "Nombre para mostrar"
}
},
"vehicles": {

View File

@@ -1,5 +1,45 @@
{
"translation": {
"appointments": {
"actions": {
"cancel": "annuler",
"intake": "Admission",
"reschedule": "Replanifier",
"viewjob": "Voir le travail"
},
"errors": {
"canceling": "Erreur lors de l'annulation du rendez-vous.",
"saving": "Erreur lors de la planification du rendez-vous. {{message}}"
},
"labels": {
"arrivedon": "Arrivé le:",
"cancelledappointment": "Rendez-vous annulé pour:",
"nodateselected": "Aucune date n'a été sélectionnée.",
"priorappointments": "Rendez-vous précédents",
"scheduledfor": "Rendez-vous prévu pour:"
},
"successes": {
"canceled": "Rendez-vous annulé avec succès.",
"created": "Rendez-vous planifié avec succès."
}
},
"associations": {
"actions": {
"activate": "Activer"
},
"fields": {
"active": "Actif?",
"shopname": "nom de la boutique"
},
"labels": {
"actions": "actes"
}
},
"bodyshop": {
"errors": {
"loading": "Impossible de charger les détails de la boutique. Veuillez appeler le support technique."
}
},
"documents": {
"errors": {
"deletes3": "Erreur lors de la suppression du document du stockage.",
@@ -52,9 +92,11 @@
"actions": {
"addDocuments": "Ajouter des documents de travail",
"addNote": "Ajouter une note",
"changestatus": "Changer le statut",
"convert": "Convertir",
"postInvoices": "Poster des factures",
"printCenter": "Centre d'impression"
"printCenter": "Centre d'impression",
"schedule": "Programme"
},
"errors": {
"creating": "",
@@ -113,8 +155,8 @@
"owner": "Propriétaire",
"owner_owing": "Cust. Owes",
"ownr_ea": "Email",
"ownr_ph1": "Téléphone 1",
"pay_date": "Date d'inspection",
"phone1": "Téléphone 1",
"phoneshort": "PH",
"policy_no": "Politique #",
"ponumber": "Numéro de bon de commande",
@@ -163,6 +205,7 @@
"vehicle": "Véhicule"
},
"labels": {
"appointmentconfirmation": "Envoyer une confirmation au client?",
"available_new_jobs": "",
"cards": {
"appraiser": "Expert",
@@ -191,7 +234,7 @@
"converted": "Travail converti avec succès.",
"created": "Le travail a été créé avec succès. Clique pour voir.",
"deleted": "Le travail a bien été supprimé.",
"save": "Enregistrement enregistré",
"save": "Le travail a été enregistré avec succès.",
"savetitle": "Enregistrement enregistré avec succès."
}
},
@@ -204,7 +247,8 @@
"activejobs": "Emplois actifs",
"availablejobs": "Emplois disponibles",
"home": "Accueil",
"jobs": "Emplois"
"jobs": "Emplois",
"schedule": "Programme"
},
"jobsdetail": {
"claimdetail": "Détails de la réclamation",
@@ -267,11 +311,16 @@
"jobsavailable": "Emplois disponibles | $t(titles.app)",
"jobsdetail": "Travail {{ro_number}} | $t(titles.app)",
"jobsdocuments": "Documents de travail {{ro_number}} | $ t (titres.app)",
"profile": "Mon profil | $t(titles.app)"
"profile": "Mon profil | $t(titles.app)",
"schedule": "Horaire | $t(titles.app)"
},
"user": {
"actions": {
"signout": "Déconnexion"
"signout": "Déconnexion",
"updateprofile": "Mettre à jour le profil"
},
"fields": {
"displayname": "Afficher un nom"
}
},
"vehicles": {

170
client/src/utils/dates.js Normal file
View File

@@ -0,0 +1,170 @@
/* eslint no-fallthrough: off */
import * as dates from "date-arithmetic";
export {
milliseconds,
seconds,
minutes,
hours,
month,
startOf,
endOf,
add,
eq,
gte,
gt,
lte,
lt,
inRange,
min,
max
} from "date-arithmetic";
const MILLI = {
seconds: 1000,
minutes: 1000 * 60,
hours: 1000 * 60 * 60,
day: 1000 * 60 * 60 * 24
};
const MONTHS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
export function monthsInYear(year) {
let date = new Date(year, 0, 1);
return MONTHS.map(i => dates.month(date, i));
}
export function firstVisibleDay(date, localizer) {
let firstOfMonth = dates.startOf(date, "month");
return dates.startOf(firstOfMonth, "week", localizer.startOfWeek());
}
export function lastVisibleDay(date, localizer) {
let endOfMonth = dates.endOf(date, "month");
return dates.endOf(endOfMonth, "week", localizer.startOfWeek());
}
export function visibleDays(date, localizer) {
let current = firstVisibleDay(date, localizer),
last = lastVisibleDay(date, localizer),
days = [];
while (dates.lte(current, last, "day")) {
days.push(current);
current = dates.add(current, 1, "day");
}
return days;
}
export function ceil(date, unit) {
let floor = dates.startOf(date, unit);
return dates.eq(floor, date) ? floor : dates.add(floor, 1, unit);
}
export function range(start, end, unit = "day") {
let current = start,
days = [];
while (dates.lte(current, end, unit)) {
days.push(current);
current = dates.add(current, 1, unit);
}
return days;
}
export function merge(date, time) {
if (time == null && date == null) return null;
if (time == null) time = new Date();
if (date == null) date = new Date();
date = dates.startOf(date, "day");
date = dates.hours(date, dates.hours(time));
date = dates.minutes(date, dates.minutes(time));
date = dates.seconds(date, dates.seconds(time));
return dates.milliseconds(date, dates.milliseconds(time));
}
export function eqTime(dateA, dateB) {
return (
dates.hours(dateA) === dates.hours(dateB) &&
dates.minutes(dateA) === dates.minutes(dateB) &&
dates.seconds(dateA) === dates.seconds(dateB)
);
}
export function isJustDate(date) {
return (
dates.hours(date) === 0 &&
dates.minutes(date) === 0 &&
dates.seconds(date) === 0 &&
dates.milliseconds(date) === 0
);
}
export function duration(start, end, unit, firstOfWeek) {
if (unit === "day") unit = "date";
return Math.abs(
dates[unit](start, undefined, firstOfWeek) -
dates[unit](end, undefined, firstOfWeek)
);
}
export function diff(dateA, dateB, unit) {
if (!unit || unit === "milliseconds") return Math.abs(+dateA - +dateB);
// the .round() handles an edge case
// with DST where the total won't be exact
// since one day in the range may be shorter/longer by an hour
return Math.round(
Math.abs(
+dates.startOf(dateA, unit) / MILLI[unit] -
+dates.startOf(dateB, unit) / MILLI[unit]
)
);
}
export function total(date, unit) {
let ms = date.getTime(),
div = 1;
switch (unit) {
case "week":
div *= 7;
case "day":
div *= 24;
case "hours":
div *= 60;
case "minutes":
div *= 60;
case "seconds":
div *= 1000;
}
return ms / div;
}
export function week(date) {
var d = new Date(date);
d.setHours(0, 0, 0);
d.setDate(d.getDate() + 4 - (d.getDay() || 7));
return Math.ceil(((d - new Date(d.getFullYear(), 0, 1)) / 8.64e7 + 1) / 7);
}
export function today() {
return dates.startOf(new Date(), "day");
}
export function yesterday() {
return dates.add(dates.startOf(new Date(), "day"), -1, "day");
}
export function tomorrow() {
return dates.add(dates.startOf(new Date(), "day"), 1, "day");
}

View File

@@ -1026,7 +1026,7 @@
dependencies:
regenerator-runtime "^0.13.2"
"@babel/runtime@^7.5.5":
"@babel/runtime@^7.1.5", "@babel/runtime@^7.5.5":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308"
integrity sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==
@@ -1620,6 +1620,11 @@
resolved "https://registry.yarnpkg.com/@redux-saga/types/-/types-1.1.0.tgz#0e81ce56b4883b4b2a3001ebe1ab298b84237204"
integrity sha512-afmTuJrylUU/0OtqzaRkbyYFFNgCF73Bvel/sw90pvGrWIZ+vyoIJqA6eMSoA6+nb443kTmulmBtC9NerXboNg==
"@restart/hooks@^0.3.12":
version "0.3.20"
resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.3.20.tgz#e7179ab41e5f346b2feca091261fbdad77e8bc19"
integrity sha512-Q1eeEqcxHQ4oqty7C5Me8/hzWwdCRR643nR/6EHxv8BVxLVYHe4IoWAHg8MIGkE4VtSm3/JnNhkoLJhCkLx5aw==
"@svgr/babel-plugin-add-jsx-attribute@^4.2.0":
version "4.2.0"
resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz#dadcb6218503532d6884b210e7f3c502caaa44b1"
@@ -1839,6 +1844,14 @@
"@types/prop-types" "*"
csstype "^2.2.0"
"@types/react@^16.9.11":
version "16.9.19"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.19.tgz#c842aa83ea490007d29938146ff2e4d9e4360c40"
integrity sha512-LJV97//H+zqKWMms0kvxaKYJDG05U2TtQB3chRLF8MPNs+MQh/H1aGlyDUxjaHvu08EAGerdX2z4LTBc7ns77A==
dependencies:
"@types/prop-types" "*"
csstype "^2.2.0"
"@types/stack-utils@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
@@ -3483,6 +3496,11 @@ clone-deep@^4.0.1:
kind-of "^6.0.2"
shallow-clone "^3.0.0"
clsx@^1.0.4:
version "1.1.0"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.0.tgz#62937c6adfea771247c34b54d320fb99624f5702"
integrity sha512-3avwM37fSK5oP6M5rQ9CNe99lwxhXDOeSWVPAOYF6OazUTgZCMb0yWlJpmdD74REy1gkEaFiub2ULv4fq9GUhA==
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
@@ -4103,7 +4121,7 @@ cssstyle@^1.0.0, cssstyle@^1.1.1:
dependencies:
cssom "0.3.x"
csstype@^2.2.0:
csstype@^2.2.0, csstype@^2.6.7:
version "2.6.8"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.8.tgz#0fb6fc2417ffd2816a418c9336da74d7f07db431"
integrity sha512-msVS9qTuMT5zwAGCVm4mxfrZ18BNc6Csd0oJAtiFMZ1FAx1CCvy2+5MDmYoix63LM/6NDbNtodCiGYGmFgO0dA==
@@ -4149,6 +4167,11 @@ data-urls@^1.0.0, data-urls@^1.1.0:
whatwg-mimetype "^2.2.0"
whatwg-url "^7.0.0"
date-arithmetic@^4.0.1:
version "4.1.0"
resolved "https://registry.yarnpkg.com/date-arithmetic/-/date-arithmetic-4.1.0.tgz#e5d6434e9deb71f79760a37b729e4a515e730ddf"
integrity sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg==
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -4409,6 +4432,14 @@ dom-converter@^0.2:
dependencies:
utila "~0.4"
dom-helpers@^5.1.0:
version "5.1.3"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.1.3.tgz#7233248eb3a2d1f74aafca31e52c5299cc8ce821"
integrity sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw==
dependencies:
"@babel/runtime" "^7.6.3"
csstype "^2.6.7"
dom-matches@>=1.0.1:
version "2.0.0"
resolved "https://registry.yarnpkg.com/dom-matches/-/dom-matches-2.0.0.tgz#d2728b416a87533980eb089b848d253cf23a758c"
@@ -7487,6 +7518,11 @@ locate-path@^3.0.0:
p-locate "^3.0.0"
path-exists "^3.0.0"
lodash-es@^4.17.11:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==
lodash._reinterpolate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
@@ -7689,6 +7725,11 @@ mem@^4.0.0:
mimic-fn "^2.0.0"
p-is-promise "^2.0.0"
memoize-one@^4.0.3:
version "4.1.0"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906"
integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA==
memoize-one@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
@@ -8908,6 +8949,11 @@ pnp-webpack-plugin@1.5.0:
dependencies:
ts-pnp "^1.1.2"
popper.js@^1.15.0:
version "1.16.1"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==
portfinder@^1.0.9:
version "1.0.25"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.25.tgz#254fd337ffba869f4b9d37edc298059cb4d35eca"
@@ -10342,6 +10388,24 @@ react-app-polyfill@^1.0.4:
regenerator-runtime "^0.13.3"
whatwg-fetch "^3.0.0"
react-big-calendar@^0.23.0:
version "0.23.0"
resolved "https://registry.yarnpkg.com/react-big-calendar/-/react-big-calendar-0.23.0.tgz#cc780481548fcefabb04cea05dd1a08456702a67"
integrity sha512-f/v1J/oqvEJqXcT5clJ5wzFIWKRNUeMCvLLzH6PYszcR15mUgqQb31HH+2Ev+YZHVW3YSStcZn2HWEhmvFr9ew==
dependencies:
"@babel/runtime" "^7.1.5"
clsx "^1.0.4"
date-arithmetic "^4.0.1"
dom-helpers "^5.1.0"
invariant "^2.2.4"
lodash "^4.17.11"
lodash-es "^4.17.11"
memoize-one "^4.0.3"
prop-types "^15.6.2"
react-overlays "^2.0.0-0"
uncontrollable "^7.0.0"
warning "^4.0.2"
react-chartjs-2@^2.8.0:
version "2.8.0"
resolved "https://registry.yarnpkg.com/react-chartjs-2/-/react-chartjs-2-2.8.0.tgz#1c24de91fb3755f8c4302675de7d66fdda339759"
@@ -10452,6 +10516,19 @@ react-number-format@^4.3.1:
dependencies:
prop-types "^15.7.2"
react-overlays@^2.0.0-0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-2.1.1.tgz#ffe2090c4a10da6b8947a1c7b1a67d0457648a0d"
integrity sha512-gaQJwmb8Ij2IGVt4D1HmLtl4A0mDVYxlsv/8i0dHWK7Mw0kNat6ORelbbEWzaXTK1TqMeQtJw/jraL3WOADz3w==
dependencies:
"@babel/runtime" "^7.4.5"
"@restart/hooks" "^0.3.12"
dom-helpers "^5.1.0"
popper.js "^1.15.0"
prop-types "^15.7.2"
uncontrollable "^7.0.0"
warning "^4.0.3"
react-popopo@^2.1.9:
version "2.1.9"
resolved "https://registry.yarnpkg.com/react-popopo/-/react-popopo-2.1.9.tgz#d93f70a8fb68227907d00c0cea4d8f5d321053ea"
@@ -12389,6 +12466,16 @@ uglify-js@^3.1.4:
commander "~2.20.3"
source-map "~0.6.1"
uncontrollable@^7.0.0:
version "7.1.1"
resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-7.1.1.tgz#f67fed3ef93637126571809746323a9db815d556"
integrity sha512-EcPYhot3uWTS3w00R32R2+vS8Vr53tttrvMj/yA1uYRhf8hbTG2GyugGqWDY0qIskxn0uTTojVd6wPYW9ZEf8Q==
dependencies:
"@babel/runtime" "^7.6.3"
"@types/react" "^16.9.11"
invariant "^2.2.4"
react-lifecycles-compat "^3.0.4"
unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"

View File

@@ -0,0 +1,3 @@
- args:
sql: DROP TABLE "public"."employees"
type: run_sql

View File

@@ -0,0 +1,21 @@
- args:
sql: CREATE EXTENSION IF NOT EXISTS pgcrypto;
type: run_sql
- args:
sql: "CREATE TABLE \"public\".\"employees\"(\"id\" uuid NOT NULL DEFAULT gen_random_uuid(),
\"created_at\" timestamptz NOT NULL DEFAULT now(), \"updated_at\" timestamptz
NOT NULL DEFAULT now(), \"first_name\" text NOT NULL, \"last_name\" text NOT
NULL, \"employee_number\" text, \"shopid\" uuid NOT NULL, \"active\" boolean
NOT NULL DEFAULT true, PRIMARY KEY (\"id\") , FOREIGN KEY (\"shopid\") REFERENCES
\"public\".\"bodyshops\"(\"id\") ON UPDATE cascade ON DELETE cascade);\nCREATE
OR REPLACE FUNCTION \"public\".\"set_current_timestamp_updated_at\"()\nRETURNS
TRIGGER AS $$\nDECLARE\n _new record;\nBEGIN\n _new := NEW;\n _new.\"updated_at\"
= NOW();\n RETURN _new;\nEND;\n$$ LANGUAGE plpgsql;\nCREATE TRIGGER \"set_public_employees_updated_at\"\nBEFORE
UPDATE ON \"public\".\"employees\"\nFOR EACH ROW\nEXECUTE PROCEDURE \"public\".\"set_current_timestamp_updated_at\"();\nCOMMENT
ON TRIGGER \"set_public_employees_updated_at\" ON \"public\".\"employees\" \nIS
'trigger to set value of column \"updated_at\" to current timestamp on row update';\n"
type: run_sql
- args:
name: employees
schema: public
type: add_existing_table_or_view

View File

@@ -0,0 +1,12 @@
- args:
relationship: employees
table:
name: bodyshops
schema: public
type: drop_relationship
- args:
relationship: bodyshop
table:
name: employees
schema: public
type: drop_relationship

View File

@@ -0,0 +1,20 @@
- args:
name: employees
table:
name: bodyshops
schema: public
using:
foreign_key_constraint_on:
column: shopid
table:
name: employees
schema: public
type: create_array_relationship
- args:
name: bodyshop
table:
name: employees
schema: public
using:
foreign_key_constraint_on: shopid
type: create_object_relationship

View File

@@ -0,0 +1,6 @@
- args:
role: user
table:
name: employees
schema: public
type: drop_insert_permission

View File

@@ -0,0 +1,30 @@
- args:
permission:
allow_upsert: true
check:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- active
- employee_number
- first_name
- last_name
- created_at
- updated_at
- id
- shopid
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: employees
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,6 @@
- args:
role: user
table:
name: employees
schema: public
type: drop_select_permission

View File

@@ -0,0 +1,27 @@
- args:
permission:
allow_aggregations: false
columns:
- active
- employee_number
- first_name
- last_name
- created_at
- updated_at
- id
- shopid
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
limit: null
role: user
table:
name: employees
schema: public
type: create_select_permission

View File

@@ -0,0 +1,6 @@
- args:
role: user
table:
name: employees
schema: public
type: drop_update_permission

View File

@@ -0,0 +1,29 @@
- args:
permission:
columns:
- active
- employee_number
- first_name
- last_name
- created_at
- updated_at
- id
- shopid
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: employees
schema: public
type: create_update_permission

View File

@@ -0,0 +1,6 @@
- args:
role: user
table:
name: employees
schema: public
type: drop_delete_permission

View File

@@ -0,0 +1,16 @@
- args:
permission:
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
role: user
table:
name: employees
schema: public
type: create_delete_permission

View File

@@ -0,0 +1,3 @@
- args:
sql: DROP TABLE "public"."appointments"
type: run_sql

View File

@@ -0,0 +1,21 @@
- args:
sql: CREATE EXTENSION IF NOT EXISTS pgcrypto;
type: run_sql
- args:
sql: "CREATE TABLE \"public\".\"appointments\"(\"id\" uuid NOT NULL DEFAULT gen_random_uuid(),
\"created_at\" timestamptz NOT NULL DEFAULT now(), \"updated_at\" timestamptz
NOT NULL DEFAULT now(), \"jobid\" uuid NOT NULL, \"start\" timestamptz NOT NULL,
\"end\" timestamptz NOT NULL, PRIMARY KEY (\"id\") , FOREIGN KEY (\"jobid\")
REFERENCES \"public\".\"jobs\"(\"id\") ON UPDATE cascade ON DELETE cascade);\nCREATE
OR REPLACE FUNCTION \"public\".\"set_current_timestamp_updated_at\"()\nRETURNS
TRIGGER AS $$\nDECLARE\n _new record;\nBEGIN\n _new := NEW;\n _new.\"updated_at\"
= NOW();\n RETURN _new;\nEND;\n$$ LANGUAGE plpgsql;\nCREATE TRIGGER \"set_public_appointments_updated_at\"\nBEFORE
UPDATE ON \"public\".\"appointments\"\nFOR EACH ROW\nEXECUTE PROCEDURE \"public\".\"set_current_timestamp_updated_at\"();\nCOMMENT
ON TRIGGER \"set_public_appointments_updated_at\" ON \"public\".\"appointments\"
\nIS 'trigger to set value of column \"updated_at\" to current timestamp on
row update';\n"
type: run_sql
- args:
name: appointments
schema: public
type: add_existing_table_or_view

View File

@@ -0,0 +1,12 @@
- args:
relationship: job
table:
name: appointments
schema: public
type: drop_relationship
- args:
relationship: appointments
table:
name: jobs
schema: public
type: drop_relationship

View File

@@ -0,0 +1,20 @@
- args:
name: job
table:
name: appointments
schema: public
using:
foreign_key_constraint_on: jobid
type: create_object_relationship
- args:
name: appointments
table:
name: jobs
schema: public
using:
foreign_key_constraint_on:
column: jobid
table:
name: appointments
schema: public
type: create_array_relationship

View File

@@ -0,0 +1,6 @@
- args:
role: user
table:
name: appointments
schema: public
type: drop_insert_permission

View File

@@ -0,0 +1,29 @@
- args:
permission:
allow_upsert: true
check:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- created_at
- end
- start
- updated_at
- id
- jobid
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: appointments
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,6 @@
- args:
role: user
table:
name: appointments
schema: public
type: drop_select_permission

View File

@@ -0,0 +1,26 @@
- args:
permission:
allow_aggregations: false
columns:
- created_at
- end
- start
- updated_at
- id
- jobid
filter:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
limit: null
role: user
table:
name: appointments
schema: public
type: create_select_permission

View File

@@ -0,0 +1,6 @@
- args:
role: user
table:
name: appointments
schema: public
type: drop_update_permission

View File

@@ -0,0 +1,28 @@
- args:
permission:
columns:
- created_at
- end
- start
- updated_at
- id
- jobid
filter:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: appointments
schema: public
type: create_update_permission

View File

@@ -0,0 +1,6 @@
- args:
role: user
table:
name: appointments
schema: public
type: drop_delete_permission

View File

@@ -0,0 +1,17 @@
- args:
permission:
filter:
job:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
role: user
table:
name: appointments
schema: public
type: create_delete_permission

View File

@@ -0,0 +1,244 @@
- args:
role: user
table:
name: jobs
schema: public
type: drop_insert_permission
- args:
permission:
check:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- id
- created_at
- updated_at
- shopid
- ro_number
- ownerid
- vehicleid
- labor_rate_id
- labor_rate_desc
- rate_lab
- rate_lad
- rate_lae
- rate_lar
- rate_las
- rate_laf
- rate_lam
- rate_lag
- rate_atp
- rate_lau
- rate_la1
- rate_la2
- rate_la3
- rate_la4
- rate_mapa
- rate_mash
- rate_mahw
- rate_ma2s
- rate_ma3s
- rate_ma2t
- rate_mabl
- rate_macs
- rate_matd
- federal_tax_rate
- state_tax_rate
- local_tax_rate
- est_co_nm
- est_addr1
- est_addr2
- est_city
- est_st
- est_zip
- est_ctry
- est_ph1
- est_ea
- est_ct_ln
- est_ct_fn
- scheduled_in
- actual_in
- scheduled_completion
- actual_completion
- scheduled_delivery
- actual_delivery
- regie_number
- invoice_date
- claim_total
- deductible
- inproduction
- statusid
- ins_co_id
- ins_co_nm
- ins_addr1
- ins_addr2
- ins_city
- ins_st
- ins_zip
- ins_ctry
- ins_ph1
- ins_ph1x
- ins_ph2
- ins_ph2x
- ins_fax
- ins_faxx
- ins_ct_ln
- ins_ct_fn
- ins_title
- ins_ct_ph
- ins_ct_phx
- ins_ea
- ins_memo
- policy_no
- ded_amt
- ded_status
- asgn_no
- asgn_date
- asgn_type
- clm_no
- clm_ofc_id
- date_estimated
- date_open
- date_scheduled
- date_invoiced
- date_closed
- date_exported
- clm_total
- owner_owing
- converted
- ciecaid
- loss_date
- clm_ofc_nm
- clm_addr1
- clm_addr2
- clm_city
- clm_st
- clm_zip
- clm_ctry
- clm_ph1
- clm_ph1x
- clm_ph2
- clm_ph2x
- clm_fax
- clm_faxx
- clm_ct_ln
- clm_ct_fn
- clm_title
- clm_ct_ph
- clm_ct_phx
- clm_ea
- payee_nms
- pay_type
- pay_date
- pay_chknm
- pay_amt
- agt_co_id
- agt_co_nm
- agt_addr1
- agt_addr2
- agt_city
- agt_st
- agt_zip
- agt_ctry
- agt_ph1
- agt_ph1x
- agt_ph2
- agt_ph2x
- agt_fax
- agt_faxx
- agt_ct_ln
- agt_ct_fn
- agt_ct_ph
- agt_ct_phx
- agt_ea
- agt_lic_no
- loss_type
- loss_desc
- theft_ind
- cat_no
- tlos_ind
- cust_pr
- insd_ln
- insd_fn
- insd_title
- insd_co_nm
- insd_addr1
- insd_addr2
- insd_city
- insd_st
- insd_zip
- insd_ctry
- insd_ph1
- insd_ph1x
- insd_ph2
- insd_ph2x
- insd_fax
- insd_faxx
- insd_ea
- ownr_ln
- ownr_fn
- ownr_title
- ownr_co_nm
- ownr_addr1
- ownr_addr2
- ownr_city
- ownr_st
- ownr_zip
- ownr_ctry
- ownr_ph1
- ownr_ph1x
- ownr_ph2
- ownr_ph2x
- ownr_fax
- ownr_faxx
- ownr_ea
- area_of_damage
- loss_cat
- est_number
- service_car
- special_coverage_policy
- csr
- po_number
- unit_number
- kmin
- kmout
- referral_source
- selling_dealer
- servicing_dealer
- servicing_dealer_contact
- selling_dealer_contact
- depreciation_taxes
- federal_tax_payable
- other_amount_payable
- towing_payable
- storage_payable
- adjustment_bottom_line
- tax_pstthr
- tax_tow_rt
- tax_sub_rt
- tax_paint_mat_rt
- tax_levies_rt
- tax_prethr
- tax_thramt
- tax_str_rt
- tax_lbr_rt
- adj_g_disc
- adj_towdis
- adj_strdis
- tax_predis
- rate_laa
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: jobs
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,243 @@
- args:
role: user
table:
name: jobs
schema: public
type: drop_insert_permission
- args:
permission:
check:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
columns:
- actual_completion
- actual_delivery
- actual_in
- adj_g_disc
- adj_strdis
- adj_towdis
- adjustment_bottom_line
- agt_addr1
- agt_addr2
- agt_city
- agt_co_id
- agt_co_nm
- agt_ct_fn
- agt_ct_ln
- agt_ct_ph
- agt_ct_phx
- agt_ctry
- agt_ea
- agt_fax
- agt_faxx
- agt_lic_no
- agt_ph1
- agt_ph1x
- agt_ph2
- agt_ph2x
- agt_st
- agt_zip
- area_of_damage
- asgn_date
- asgn_no
- asgn_type
- cat_no
- ciecaid
- clm_addr1
- clm_addr2
- clm_city
- clm_ct_fn
- clm_ct_ln
- clm_ct_ph
- clm_ct_phx
- clm_ctry
- clm_ea
- clm_fax
- clm_faxx
- clm_no
- clm_ofc_id
- clm_ofc_nm
- clm_ph1
- clm_ph1x
- clm_ph2
- clm_ph2x
- clm_st
- clm_title
- clm_total
- clm_zip
- converted
- created_at
- csr
- cust_pr
- date_closed
- date_estimated
- date_exported
- date_invoiced
- date_open
- date_scheduled
- ded_amt
- ded_status
- deductible
- depreciation_taxes
- est_addr1
- est_addr2
- est_city
- est_co_nm
- est_ct_fn
- est_ct_ln
- est_ctry
- est_ea
- est_number
- est_ph1
- est_st
- est_zip
- federal_tax_payable
- federal_tax_rate
- id
- inproduction
- ins_addr1
- ins_addr2
- ins_city
- ins_co_id
- ins_co_nm
- ins_ct_fn
- ins_ct_ln
- ins_ct_ph
- ins_ct_phx
- ins_ctry
- ins_ea
- ins_fax
- ins_faxx
- ins_memo
- ins_ph1
- ins_ph1x
- ins_ph2
- ins_ph2x
- ins_st
- ins_title
- ins_zip
- insd_addr1
- insd_addr2
- insd_city
- insd_co_nm
- insd_ctry
- insd_ea
- insd_fax
- insd_faxx
- insd_fn
- insd_ln
- insd_ph1
- insd_ph1x
- insd_ph2
- insd_ph2x
- insd_st
- insd_title
- insd_zip
- invoice_date
- kmin
- kmout
- labor_rate_desc
- labor_rate_id
- local_tax_rate
- loss_cat
- loss_date
- loss_desc
- loss_type
- other_amount_payable
- owner_owing
- ownerid
- ownr_addr1
- ownr_addr2
- ownr_city
- ownr_co_nm
- ownr_ctry
- ownr_ea
- ownr_fax
- ownr_faxx
- ownr_fn
- ownr_ln
- ownr_ph1
- ownr_ph1x
- ownr_ph2
- ownr_ph2x
- ownr_st
- ownr_title
- ownr_zip
- pay_amt
- pay_chknm
- pay_date
- pay_type
- payee_nms
- po_number
- policy_no
- rate_atp
- rate_la1
- rate_la2
- rate_la3
- rate_la4
- rate_laa
- rate_lab
- rate_lad
- rate_lae
- rate_laf
- rate_lag
- rate_lam
- rate_lar
- rate_las
- rate_lau
- rate_ma2s
- rate_ma2t
- rate_ma3s
- rate_mabl
- rate_macs
- rate_mahw
- rate_mapa
- rate_mash
- rate_matd
- referral_source
- regie_number
- ro_number
- scheduled_completion
- scheduled_delivery
- scheduled_in
- selling_dealer
- selling_dealer_contact
- service_car
- servicing_dealer
- servicing_dealer_contact
- shopid
- special_coverage_policy
- state_tax_rate
- statusid
- storage_payable
- tax_lbr_rt
- tax_levies_rt
- tax_paint_mat_rt
- tax_predis
- tax_prethr
- tax_pstthr
- tax_str_rt
- tax_sub_rt
- tax_thramt
- tax_tow_rt
- theft_ind
- tlos_ind
- towing_payable
- unit_number
- updated_at
- vehicleid
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: jobs
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,242 @@
- args:
role: user
table:
name: jobs
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- converted
- inproduction
- special_coverage_policy
- theft_ind
- tlos_ind
- asgn_date
- invoice_date
- loss_date
- pay_date
- kmin
- kmout
- est_number
- area_of_damage
- adj_g_disc
- adj_strdis
- adj_towdis
- adjustment_bottom_line
- claim_total
- clm_total
- ded_amt
- deductible
- depreciation_taxes
- federal_tax_payable
- federal_tax_rate
- local_tax_rate
- other_amount_payable
- owner_owing
- pay_amt
- rate_atp
- rate_la1
- rate_la2
- rate_la3
- rate_la4
- rate_laa
- rate_lab
- rate_lad
- rate_lae
- rate_laf
- rate_lag
- rate_lam
- rate_lar
- rate_las
- rate_lau
- rate_ma2s
- rate_ma2t
- rate_ma3s
- rate_mabl
- rate_macs
- rate_mahw
- rate_mapa
- rate_mash
- rate_matd
- state_tax_rate
- storage_payable
- tax_lbr_rt
- tax_levies_rt
- tax_paint_mat_rt
- tax_predis
- tax_prethr
- tax_str_rt
- tax_thramt
- towing_payable
- agt_addr1
- agt_addr2
- agt_city
- agt_co_id
- agt_co_nm
- agt_ct_fn
- agt_ct_ln
- agt_ct_ph
- agt_ct_phx
- agt_ctry
- agt_ea
- agt_fax
- agt_faxx
- agt_lic_no
- agt_ph1
- agt_ph1x
- agt_ph2
- agt_ph2x
- agt_st
- agt_zip
- asgn_no
- asgn_type
- cat_no
- ciecaid
- clm_addr1
- clm_addr2
- clm_city
- clm_ct_fn
- clm_ct_ln
- clm_ct_ph
- clm_ct_phx
- clm_ctry
- clm_ea
- clm_fax
- clm_faxx
- clm_no
- clm_ofc_id
- clm_ofc_nm
- clm_ph1
- clm_ph1x
- clm_ph2
- clm_ph2x
- clm_st
- clm_title
- clm_zip
- csr
- cust_pr
- ded_status
- est_addr1
- est_addr2
- est_city
- est_co_nm
- est_ct_fn
- est_ct_ln
- est_ctry
- est_ea
- est_ph1
- est_st
- est_zip
- ins_addr1
- ins_addr2
- ins_city
- ins_co_id
- ins_co_nm
- ins_ct_fn
- ins_ct_ln
- ins_ct_ph
- ins_ct_phx
- ins_ctry
- insd_addr1
- insd_addr2
- insd_city
- insd_co_nm
- insd_ctry
- insd_ea
- insd_fax
- insd_faxx
- insd_fn
- insd_ln
- insd_ph1
- insd_ph1x
- insd_ph2
- insd_ph2x
- insd_st
- insd_title
- insd_zip
- ins_ea
- ins_fax
- ins_faxx
- ins_memo
- ins_ph1
- ins_ph1x
- ins_ph2
- ins_ph2x
- ins_st
- ins_title
- ins_zip
- labor_rate_desc
- labor_rate_id
- loss_cat
- loss_desc
- loss_type
- ownr_addr1
- ownr_addr2
- ownr_city
- ownr_co_nm
- ownr_ctry
- ownr_ea
- ownr_fax
- ownr_faxx
- ownr_fn
- ownr_ln
- ownr_ph1
- ownr_ph1x
- ownr_ph2
- ownr_ph2x
- ownr_st
- ownr_title
- ownr_zip
- pay_chknm
- payee_nms
- pay_type
- policy_no
- po_number
- referral_source
- regie_number
- ro_number
- selling_dealer
- selling_dealer_contact
- service_car
- servicing_dealer
- servicing_dealer_contact
- unit_number
- actual_completion
- actual_delivery
- actual_in
- created_at
- date_closed
- date_estimated
- date_exported
- date_invoiced
- date_open
- date_scheduled
- scheduled_completion
- scheduled_delivery
- scheduled_in
- updated_at
- id
- ownerid
- shopid
- statusid
- vehicleid
- tax_pstthr
- tax_sub_rt
- tax_tow_rt
computed_fields: []
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
role: user
table:
name: jobs
schema: public
type: create_select_permission

View File

@@ -0,0 +1,241 @@
- args:
role: user
table:
name: jobs
schema: public
type: drop_select_permission
- args:
permission:
allow_aggregations: false
columns:
- actual_completion
- actual_delivery
- actual_in
- adj_g_disc
- adj_strdis
- adj_towdis
- adjustment_bottom_line
- agt_addr1
- agt_addr2
- agt_city
- agt_co_id
- agt_co_nm
- agt_ct_fn
- agt_ct_ln
- agt_ct_ph
- agt_ct_phx
- agt_ctry
- agt_ea
- agt_fax
- agt_faxx
- agt_lic_no
- agt_ph1
- agt_ph1x
- agt_ph2
- agt_ph2x
- agt_st
- agt_zip
- area_of_damage
- asgn_date
- asgn_no
- asgn_type
- cat_no
- ciecaid
- clm_addr1
- clm_addr2
- clm_city
- clm_ct_fn
- clm_ct_ln
- clm_ct_ph
- clm_ct_phx
- clm_ctry
- clm_ea
- clm_fax
- clm_faxx
- clm_no
- clm_ofc_id
- clm_ofc_nm
- clm_ph1
- clm_ph1x
- clm_ph2
- clm_ph2x
- clm_st
- clm_title
- clm_total
- clm_zip
- converted
- created_at
- csr
- cust_pr
- date_closed
- date_estimated
- date_exported
- date_invoiced
- date_open
- date_scheduled
- ded_amt
- ded_status
- deductible
- depreciation_taxes
- est_addr1
- est_addr2
- est_city
- est_co_nm
- est_ct_fn
- est_ct_ln
- est_ctry
- est_ea
- est_number
- est_ph1
- est_st
- est_zip
- federal_tax_payable
- federal_tax_rate
- id
- inproduction
- ins_addr1
- ins_addr2
- ins_city
- ins_co_id
- ins_co_nm
- ins_ct_fn
- ins_ct_ln
- ins_ct_ph
- ins_ct_phx
- ins_ctry
- ins_ea
- ins_fax
- ins_faxx
- ins_memo
- ins_ph1
- ins_ph1x
- ins_ph2
- ins_ph2x
- ins_st
- ins_title
- ins_zip
- insd_addr1
- insd_addr2
- insd_city
- insd_co_nm
- insd_ctry
- insd_ea
- insd_fax
- insd_faxx
- insd_fn
- insd_ln
- insd_ph1
- insd_ph1x
- insd_ph2
- insd_ph2x
- insd_st
- insd_title
- insd_zip
- invoice_date
- kmin
- kmout
- labor_rate_desc
- labor_rate_id
- local_tax_rate
- loss_cat
- loss_date
- loss_desc
- loss_type
- other_amount_payable
- owner_owing
- ownerid
- ownr_addr1
- ownr_addr2
- ownr_city
- ownr_co_nm
- ownr_ctry
- ownr_ea
- ownr_fax
- ownr_faxx
- ownr_fn
- ownr_ln
- ownr_ph1
- ownr_ph1x
- ownr_ph2
- ownr_ph2x
- ownr_st
- ownr_title
- ownr_zip
- pay_amt
- pay_chknm
- pay_date
- pay_type
- payee_nms
- po_number
- policy_no
- rate_atp
- rate_la1
- rate_la2
- rate_la3
- rate_la4
- rate_laa
- rate_lab
- rate_lad
- rate_lae
- rate_laf
- rate_lag
- rate_lam
- rate_lar
- rate_las
- rate_lau
- rate_ma2s
- rate_ma2t
- rate_ma3s
- rate_mabl
- rate_macs
- rate_mahw
- rate_mapa
- rate_mash
- rate_matd
- referral_source
- regie_number
- ro_number
- scheduled_completion
- scheduled_delivery
- scheduled_in
- selling_dealer
- selling_dealer_contact
- service_car
- servicing_dealer
- servicing_dealer_contact
- shopid
- special_coverage_policy
- state_tax_rate
- statusid
- storage_payable
- tax_lbr_rt
- tax_levies_rt
- tax_paint_mat_rt
- tax_predis
- tax_prethr
- tax_pstthr
- tax_str_rt
- tax_sub_rt
- tax_thramt
- tax_tow_rt
- theft_ind
- tlos_ind
- towing_payable
- unit_number
- updated_at
- vehicleid
computed_fields: []
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
role: user
table:
name: jobs
schema: public
type: create_select_permission

View File

@@ -0,0 +1,244 @@
- args:
role: user
table:
name: jobs
schema: public
type: drop_update_permission
- args:
permission:
columns:
- converted
- inproduction
- special_coverage_policy
- theft_ind
- tlos_ind
- asgn_date
- invoice_date
- loss_date
- pay_date
- kmin
- kmout
- est_number
- area_of_damage
- adj_g_disc
- adj_strdis
- adj_towdis
- adjustment_bottom_line
- claim_total
- clm_total
- ded_amt
- deductible
- depreciation_taxes
- federal_tax_payable
- federal_tax_rate
- local_tax_rate
- other_amount_payable
- owner_owing
- pay_amt
- rate_atp
- rate_la1
- rate_la2
- rate_la3
- rate_la4
- rate_laa
- rate_lab
- rate_lad
- rate_lae
- rate_laf
- rate_lag
- rate_lam
- rate_lar
- rate_las
- rate_lau
- rate_ma2s
- rate_ma2t
- rate_ma3s
- rate_mabl
- rate_macs
- rate_mahw
- rate_mapa
- rate_mash
- rate_matd
- state_tax_rate
- storage_payable
- tax_lbr_rt
- tax_levies_rt
- tax_paint_mat_rt
- tax_predis
- tax_prethr
- tax_str_rt
- tax_thramt
- towing_payable
- agt_addr1
- agt_addr2
- agt_city
- agt_co_id
- agt_co_nm
- agt_ct_fn
- agt_ct_ln
- agt_ct_ph
- agt_ct_phx
- agt_ctry
- agt_ea
- agt_fax
- agt_faxx
- agt_lic_no
- agt_ph1
- agt_ph1x
- agt_ph2
- agt_ph2x
- agt_st
- agt_zip
- asgn_no
- asgn_type
- cat_no
- ciecaid
- clm_addr1
- clm_addr2
- clm_city
- clm_ct_fn
- clm_ct_ln
- clm_ct_ph
- clm_ct_phx
- clm_ctry
- clm_ea
- clm_fax
- clm_faxx
- clm_no
- clm_ofc_id
- clm_ofc_nm
- clm_ph1
- clm_ph1x
- clm_ph2
- clm_ph2x
- clm_st
- clm_title
- clm_zip
- csr
- cust_pr
- ded_status
- est_addr1
- est_addr2
- est_city
- est_co_nm
- est_ct_fn
- est_ct_ln
- est_ctry
- est_ea
- est_ph1
- est_st
- est_zip
- ins_addr1
- ins_addr2
- ins_city
- ins_co_id
- ins_co_nm
- ins_ct_fn
- ins_ct_ln
- ins_ct_ph
- ins_ct_phx
- ins_ctry
- insd_addr1
- insd_addr2
- insd_city
- insd_co_nm
- insd_ctry
- insd_ea
- insd_fax
- insd_faxx
- insd_fn
- insd_ln
- insd_ph1
- insd_ph1x
- insd_ph2
- insd_ph2x
- insd_st
- insd_title
- insd_zip
- ins_ea
- ins_fax
- ins_faxx
- ins_memo
- ins_ph1
- ins_ph1x
- ins_ph2
- ins_ph2x
- ins_st
- ins_title
- ins_zip
- labor_rate_desc
- labor_rate_id
- loss_cat
- loss_desc
- loss_type
- ownr_addr1
- ownr_addr2
- ownr_city
- ownr_co_nm
- ownr_ctry
- ownr_ea
- ownr_fax
- ownr_faxx
- ownr_fn
- ownr_ln
- ownr_ph1
- ownr_ph1x
- ownr_ph2
- ownr_ph2x
- ownr_st
- ownr_title
- ownr_zip
- pay_chknm
- payee_nms
- pay_type
- policy_no
- po_number
- referral_source
- regie_number
- ro_number
- selling_dealer
- selling_dealer_contact
- service_car
- servicing_dealer
- servicing_dealer_contact
- unit_number
- actual_completion
- actual_delivery
- actual_in
- created_at
- date_closed
- date_estimated
- date_exported
- date_invoiced
- date_open
- date_scheduled
- scheduled_completion
- scheduled_delivery
- scheduled_in
- updated_at
- id
- ownerid
- shopid
- statusid
- vehicleid
- tax_pstthr
- tax_sub_rt
- tax_tow_rt
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: jobs
schema: public
type: create_update_permission

View File

@@ -0,0 +1,243 @@
- args:
role: user
table:
name: jobs
schema: public
type: drop_update_permission
- args:
permission:
columns:
- actual_completion
- actual_delivery
- actual_in
- adj_g_disc
- adj_strdis
- adj_towdis
- adjustment_bottom_line
- agt_addr1
- agt_addr2
- agt_city
- agt_co_id
- agt_co_nm
- agt_ct_fn
- agt_ct_ln
- agt_ct_ph
- agt_ct_phx
- agt_ctry
- agt_ea
- agt_fax
- agt_faxx
- agt_lic_no
- agt_ph1
- agt_ph1x
- agt_ph2
- agt_ph2x
- agt_st
- agt_zip
- area_of_damage
- asgn_date
- asgn_no
- asgn_type
- cat_no
- ciecaid
- clm_addr1
- clm_addr2
- clm_city
- clm_ct_fn
- clm_ct_ln
- clm_ct_ph
- clm_ct_phx
- clm_ctry
- clm_ea
- clm_fax
- clm_faxx
- clm_no
- clm_ofc_id
- clm_ofc_nm
- clm_ph1
- clm_ph1x
- clm_ph2
- clm_ph2x
- clm_st
- clm_title
- clm_total
- clm_zip
- converted
- created_at
- csr
- cust_pr
- date_closed
- date_estimated
- date_exported
- date_invoiced
- date_open
- date_scheduled
- ded_amt
- ded_status
- deductible
- depreciation_taxes
- est_addr1
- est_addr2
- est_city
- est_co_nm
- est_ct_fn
- est_ct_ln
- est_ctry
- est_ea
- est_number
- est_ph1
- est_st
- est_zip
- federal_tax_payable
- federal_tax_rate
- id
- inproduction
- ins_addr1
- ins_addr2
- ins_city
- ins_co_id
- ins_co_nm
- ins_ct_fn
- ins_ct_ln
- ins_ct_ph
- ins_ct_phx
- ins_ctry
- ins_ea
- ins_fax
- ins_faxx
- ins_memo
- ins_ph1
- ins_ph1x
- ins_ph2
- ins_ph2x
- ins_st
- ins_title
- ins_zip
- insd_addr1
- insd_addr2
- insd_city
- insd_co_nm
- insd_ctry
- insd_ea
- insd_fax
- insd_faxx
- insd_fn
- insd_ln
- insd_ph1
- insd_ph1x
- insd_ph2
- insd_ph2x
- insd_st
- insd_title
- insd_zip
- invoice_date
- kmin
- kmout
- labor_rate_desc
- labor_rate_id
- local_tax_rate
- loss_cat
- loss_date
- loss_desc
- loss_type
- other_amount_payable
- owner_owing
- ownerid
- ownr_addr1
- ownr_addr2
- ownr_city
- ownr_co_nm
- ownr_ctry
- ownr_ea
- ownr_fax
- ownr_faxx
- ownr_fn
- ownr_ln
- ownr_ph1
- ownr_ph1x
- ownr_ph2
- ownr_ph2x
- ownr_st
- ownr_title
- ownr_zip
- pay_amt
- pay_chknm
- pay_date
- pay_type
- payee_nms
- po_number
- policy_no
- rate_atp
- rate_la1
- rate_la2
- rate_la3
- rate_la4
- rate_laa
- rate_lab
- rate_lad
- rate_lae
- rate_laf
- rate_lag
- rate_lam
- rate_lar
- rate_las
- rate_lau
- rate_ma2s
- rate_ma2t
- rate_ma3s
- rate_mabl
- rate_macs
- rate_mahw
- rate_mapa
- rate_mash
- rate_matd
- referral_source
- regie_number
- ro_number
- scheduled_completion
- scheduled_delivery
- scheduled_in
- selling_dealer
- selling_dealer_contact
- service_car
- servicing_dealer
- servicing_dealer_contact
- shopid
- special_coverage_policy
- state_tax_rate
- statusid
- storage_payable
- tax_lbr_rt
- tax_levies_rt
- tax_paint_mat_rt
- tax_predis
- tax_prethr
- tax_pstthr
- tax_str_rt
- tax_sub_rt
- tax_thramt
- tax_tow_rt
- theft_ind
- tlos_ind
- towing_payable
- unit_number
- updated_at
- vehicleid
filter:
bodyshop:
associations:
_and:
- user:
authid:
_eq: X-Hasura-User-Id
- active:
_eq: true
localPresets:
- key: ""
value: ""
set: {}
role: user
table:
name: jobs
schema: public
type: create_update_permission

View File

@@ -0,0 +1,9 @@
- args:
sql: ALTER TABLE "public"."jobs" ADD COLUMN "claim_total" numeric
type: run_sql
- args:
sql: ALTER TABLE "public"."jobs" ALTER COLUMN "claim_total" DROP NOT NULL
type: run_sql
- args:
sql: ALTER TABLE "public"."jobs" ALTER COLUMN "claim_total" SET DEFAULT 0
type: run_sql

View File

@@ -0,0 +1,3 @@
- args:
sql: ALTER TABLE "public"."jobs" DROP COLUMN "claim_total" CASCADE
type: run_sql

View File

@@ -0,0 +1,6 @@
- args:
sql: ALTER TABLE ONLY "public"."jobs" ALTER COLUMN "clm_total" SET DEFAULT 0;
type: run_sql
- args:
sql: COMMENT ON COLUMN "public"."jobs"."clm_total" IS E'null'
type: run_sql

View File

@@ -0,0 +1,6 @@
- args:
sql: ALTER TABLE ONLY "public"."jobs" ALTER COLUMN "clm_total" SET DEFAULT 0;
type: run_sql
- args:
sql: COMMENT ON COLUMN "public"."jobs"."clm_total" IS E''
type: run_sql

View File

@@ -0,0 +1,6 @@
- args:
role: anonymous
table:
name: users
schema: public
type: drop_select_permission

View File

@@ -0,0 +1,12 @@
- args:
permission:
allow_aggregations: false
columns:
- authid
filter: {}
limit: null
role: anonymous
table:
name: users
schema: public
type: create_select_permission

View File

@@ -0,0 +1,17 @@
- args:
permission:
columns:
- authid
- email
- created_at
- updated_at
filter: {}
localPresets:
- key: ""
value: ""
set: {}
role: anonymous
table:
name: users
schema: public
type: create_update_permission

View File

@@ -0,0 +1,6 @@
- args:
role: anonymous
table:
name: users
schema: public
type: drop_update_permission

View File

@@ -0,0 +1,17 @@
- args:
permission:
check: {}
columns:
- authid
- created_at
- email
- updated_at
localPresets:
- key: ""
value: ""
set: {}
role: anonymous
table:
name: users
schema: public
type: create_insert_permission

View File

@@ -0,0 +1,6 @@
- args:
role: anonymous
table:
name: users
schema: public
type: drop_insert_permission

View File

@@ -0,0 +1,12 @@
- args:
permission:
allow_aggregations: false
columns:
- authid
computed_fields: []
filter: {}
role: anonymous
table:
name: users
schema: public
type: create_select_permission

View File

@@ -0,0 +1,6 @@
- args:
role: anonymous
table:
name: users
schema: public
type: drop_select_permission

View File

@@ -0,0 +1,3 @@
- args:
sql: ALTER TABLE "public"."appointments" DROP COLUMN "canceled";
type: run_sql

View File

@@ -0,0 +1,4 @@
- args:
sql: ALTER TABLE "public"."appointments" ADD COLUMN "canceled" boolean NOT NULL
DEFAULT false;
type: run_sql

View File

@@ -0,0 +1,3 @@
- args:
sql: ALTER TABLE "public"."appointments" DROP COLUMN "arrived";
type: run_sql

View File

@@ -0,0 +1,4 @@
- args:
sql: ALTER TABLE "public"."appointments" ADD COLUMN "arrived" boolean NOT NULL
DEFAULT false;
type: run_sql

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