Compare commits

...

27 Commits

Author SHA1 Message Date
Patrick Fic
68c1ac3e70 Merged in feature/IO-3076-daily-usage-reports (pull request #2047)
Feature/IO-3076 daily usage reports

Approved-by: Dave Richer
2025-01-10 19:47:37 +00:00
Patrick Fic
2f267a9f2c IO-3076 updates to usage report. 2025-01-10 11:39:18 -08:00
Patrick Fic
d6fbf02092 IO-3076 Initial usage reports design. 2025-01-09 11:22:08 -08:00
Patrick Fic
7a88dd1aae Release/2025 01 10 IO-3069 IO-3067 IO-3070 2025-01-08 14:16:41 -08:00
Patrick Fic
521aa81591 Merged in feature/IO-3067-implement-learn-more-link-for-rome-upsells (pull request #2038)
Feature/IO-3067 implement learn more link for rome upsells
2025-01-08 17:52:41 +00:00
Allan Carr
abe1e80844 Merged in feature/IO-3070-Enter-Bills-Header-Missing-Translation (pull request #2033)
IO-3070 Enter Bills Header Missing Translation

Approved-by: Dave Richer
2025-01-02 16:10:16 +00:00
Allan Carr
58e897db31 Merged in feature/IO-3069-Job-Drawer-Documents-Upsell (pull request #2032)
IO-3069 Job Drawer Documents Upsell correction

Approved-by: Dave Richer
2025-01-02 16:09:59 +00:00
Patrick Fic
b7ed6734a0 Merged in revert/revert-pr-2034 (pull request #2036)
Revert "Feature/IO-3067 implement learn more link for rome upsells (pull request #2034)"
2025-01-02 16:08:29 +00:00
Patrick Fic
7d5a866a5c include intellipay. 2025-01-02 08:07:01 -08:00
Patrick Fic
23becf6494 Revert "Feature/IO-3067 implement learn more link for rome upsells (pull request #2034)" 2025-01-02 16:02:14 +00:00
Patrick Fic
64ee2c1526 Merged in feature/IO-3067-implement-learn-more-link-for-rome-upsells (pull request #2035)
IO-3067 Add learn more link for Rome.
2025-01-01 23:17:26 +00:00
Patrick Fic
c033c0fbc5 Merged in feature/IO-3067-implement-learn-more-link-for-rome-upsells (pull request #2034)
Feature/IO-3067 implement learn more link for rome upsells
2025-01-01 23:09:58 +00:00
Allan Carr
f8ddfeb7d0 IO-3070 Enter Bills Header Missing Translation
Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-12-27 10:03:51 -08:00
Allan Carr
bc42d19dff IO-3069 Job Drawer Documents Upsell correction
Would constantly display the upsell component

Signed-off-by: Allan Carr <allan.carr@thinkimex.com>
2024-12-27 10:01:11 -08:00
Allan Carr
acc6633271 Merged in hotfix/IO-3020-smart-scheduling-upsell (pull request #2029)
IO-3020 Fix PrintCenter Upsell restrictions

Approved-by: Patrick Fic
2024-12-16 20:50:51 +00:00
Patrick Fic
836e9b846a Merged in hotfix/IO-3020-smart-scheduling-upsell (pull request #2028)
IO-3020 Resolve smart scheduling upsell displays when they shouldn't.
2024-12-16 18:29:51 +00:00
Patrick Fic
fd01746f7d Merged in hotfix/IO-3001-null-cieca-scrubbing (pull request #2026)
Hotfix/IO-3001 null cieca scrubbing
2024-12-16 16:33:42 +00:00
Patrick Fic
4ec171d93b Merged in release/2024-12-13 (pull request #2022)
IO-3020 IO-3036 Remove Audit and Lifecycle feature wraps.
2024-12-13 16:28:53 +00:00
Dave Richer
608988c67c Merged in release/2024-12-13 (pull request #2020)
feature/IO-3056-Enhanced-Lightbox-Logging
2024-12-12 21:16:07 +00:00
Patrick Fic
8da4d0b0f1 Merged in release/2024-12-13 (pull request #2018)
IO-3020 IO-3036 Resolve lock wrapper on payroll allocations.
2024-12-12 20:43:04 +00:00
Patrick Fic
a54668e030 Merged in release/2024-12-13 (pull request #2016)
Release/2024 12 13
2024-12-12 17:48:33 +00:00
Dave Richer
2386457cf5 Merged in release/2024-12-13 (pull request #2013)
release/2024-12-13 into test-AIO - IO-2968
2024-12-11 18:26:57 +00:00
Patrick Fic
45944ae8c9 Merged in feature/IO-3020-IO-3036-imex-lite-rome-lite (pull request #2012)
feature/IO-3020-IO-3036-imex-lite-rome-lite

Approved-by: Patrick Fic
2024-12-11 17:45:25 +00:00
Patrick Fic
2c32a4891b Merged in feature/IO-3020-IO-3036-imex-lite-rome-lite (pull request #2011)
feature/IO-3020-IO-3036-imex-lite-rome-lite

Approved-by: Patrick Fic
2024-12-10 21:23:37 +00:00
Patrick Fic
2b9fe61d79 Merged in feature/IO-3020-IO-3036-imex-lite-rome-lite (pull request #2009)
IO-3020 IO-3036 Correct masking issue.
2024-12-10 19:03:07 +00:00
Patrick Fic
95751103a2 Merged in feature/IO-3020-IO-3036-imex-lite-rome-lite (pull request #2004)
Feature/IO-3020 IO 3036 ImEX Lite Rome Starter

Approved-by: Dave Richer
2024-12-10 17:49:56 +00:00
Allan Carr
8ca4c5d7fa Merged in hotfix/2024-12-09 (pull request #2007)
IO-3050 Adjust Customer setup
2024-12-09 19:57:21 +00:00
9 changed files with 188 additions and 18 deletions

View File

@@ -148,7 +148,7 @@ function Header({
label: (
<Space>
<LockWrapper featureName="bills" bodyshop={bodyshop}>
{t(t("menus.header.enterbills"))}
{t("menus.header.enterbills")}
</LockWrapper>
</Space>
),

View File

@@ -122,7 +122,7 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
</Col>
{!bodyshop.uselocalmediaserver && (
<Col {...span}>
<JobDetailCardsDocumentsComponent loading={loading} data={data ? data.jobs_by_pk : null} />
<JobDetailCardsDocumentsComponent loading={loading} data={data ? data.jobs_by_pk : null} bodyshop={bodyshop} />
</Col>
)}
<Col {...span}>

View File

@@ -1,12 +1,14 @@
import { Carousel } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
import { GenerateThumbUrl } from "../jobs-documents-gallery/job-documents.utility";
import CardTemplate from "./job-detail-cards.template.component";
import UpsellComponent, { upsellEnum } from "../upsell/upsell.component";
import CardTemplate from "./job-detail-cards.template.component";
export default function JobDetailCardsDocumentsComponent({ loading, data }) {
export default function JobDetailCardsDocumentsComponent({ loading, data, bodyshop }) {
const { t } = useTranslation();
const hasMediaAccess = HasFeatureAccess({ bodyshop, featureName: "media" });
if (!data)
return (
@@ -21,17 +23,19 @@ export default function JobDetailCardsDocumentsComponent({ loading, data }) {
title={t("jobs.labels.cards.documents")}
extraLink={`/manage/jobs/${data.id}?tab=documents`}
>
<UpsellComponent disableMask upsell={upsellEnum().media.general}>
{data.documents.length > 0 ? (
<Carousel autoplay>
{data.documents.map((item) => (
<img key={item.id} src={GenerateThumbUrl(item)} alt={item.name} />
))}
</Carousel>
) : (
<div>{t("documents.errors.nodocuments")}</div>
)}
</UpsellComponent>
{!hasMediaAccess && (
<UpsellComponent disableMask upsell={upsellEnum().media.general}>
{data.documents.length > 0 ? (
<Carousel autoplay>
{data.documents.map((item) => (
<img key={item.id} src={GenerateThumbUrl(item)} alt={item.name} />
))}
</Carousel>
) : (
<div>{t("documents.errors.nodocuments")}</div>
)}
</UpsellComponent>
)}
</CardTemplate>
);
}

View File

@@ -329,7 +329,8 @@ const main = async () => {
main().catch((error) => {
logger.log(`Main-API-Error: Something was not caught in the application.`, "error", "api", null, {
error: error.message,
errorjson: JSON.stringify(error)
errorjson: JSON.stringify(error),
stack: error.stack
});
// Note: If we want the app to crash on all uncaught async operations, we would
// need to put a `process.exit(1);` here

View File

@@ -3,3 +3,4 @@ exports.autohouse = require("./autohouse").default;
exports.chatter = require("./chatter").default;
exports.claimscorp = require("./claimscorp").default;
exports.kaizen = require("./kaizen").default;
exports.usageReport = require("./usageReport").default;

View File

@@ -0,0 +1,90 @@
const path = require("path");
require("dotenv").config({
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
});
const client = require("../graphql-client/graphql-client").client;
const emailer = require("../email/sendemail");
const moment = require("moment-timezone");
const converter = require("json-2-csv");
const logger = require("../utils/logger");
const queries = require("../graphql-client/queries");
const InstanceMgr = require("../utils/instanceMgr").default;
exports.default = async (req, res) => {
try {
logger.log("usage-report-email-start", "debug", req?.user?.email, null, {});
if (InstanceMgr({ rome: false, imex: true })) {
//Disable for ImEX at the moment.
res.sendStatus(403);
logger.log("usage-report-email-forbidden", "warn", req?.user?.email, null, {});
return;
}
if (process.env.NODE_ENV !== "production") {
res.sendStatus(403);
return;
}
// Validate using autohouse token header.
if (req.headers["x-imex-auth"] !== process.env.AUTOHOUSE_AUTH_TOKEN) {
res.sendStatus(401);
logger.log("usage-report-email-forbidden", "warn", req?.user?.email, null, {});
return;
}
//Query the usage data.
const queryResults = await client.request(queries.STATUS_UPDATE, {
today: moment().startOf("day").subtract(3, "days"),
period: moment().subtract(90, "days").startOf("day")
});
//Massage the data.
const shopList = queryResults.bodyshops.map((shop) => ({
"Shop Name": shop.shopname,
"Days Since Creation": moment().diff(moment(shop.created_at), "days"),
"Jobs Created": shop.jobs_created.aggregate.count,
"Jobs Updated": shop.jobs_updated.aggregate.count,
"Owners Created": shop.owners_created.aggregate.count,
"Owners Updated": shop.owners_updated.aggregate.count,
"Vehicles Created": shop.vehicles_created.aggregate.count,
"Vehicles Updated": shop.vehicles_updated.aggregate.count,
"Tasks Created": shop.tasks_created.aggregate.count,
"Tasks Updated": shop.tasks_updated.aggregate.count
}));
const csv = converter.json2csv(shopList, { emptyFieldValue: "" });
emailer
.sendTaskEmail({
to: ["patrick.fic@convenient-brands.com", "bradley.rhoades@convenient-brands.com"],
subject: `RO Usage Report - ${moment().format("MM/DD/YYYY")}`,
text: `
Usage Report for ${moment().format("MM/DD/YYYY")} for Rome Online Customers.
Notes:
- Days Since Creation: The number of days since the shop was created. Only shops created in the last 90 days are included.
- Updated values should be higher than created values.
- Counts are inclusive of the last 3 days of data.
`,
attachments: [{ filename: `RO Usage Report ${moment().format("MM/DD/YYYY")}.csv`, content: csv }]
})
.then(() => {
logger.log("usage-report-email-success", "debug", req?.user?.email, null, {
csv
});
})
.catch((error) => {
logger.log("usage-report-email-send-error", "ERROR", req?.user?.email, null, {
error: error.message,
stack: error.stack
});
});
res.sendStatus(200);
return;
} catch (error) {
logger.log("usage-report-email-error", "ERROR", req?.user?.email, null, {
error: error.message,
stack: error.stack
});
res.status(500).json({ error: error.message, stack: error.stack });
}
};

View File

@@ -2617,3 +2617,76 @@ exports.CREATE_CONVERSATION = `mutation CREATE_CONVERSATION($conversation: [conv
}
}
`;
exports.STATUS_UPDATE = `query STATUS_UPDATE($period: timestamptz!, $today: timestamptz!) {
bodyshops(where: { created_at: { _gte: $period } }) {
shopname
id
created_at
jobs_created: jobs_aggregate(where: { created_at: { _gte: $today } }) {
aggregate {
count
}
}
jobs_updated: jobs_aggregate(where: { updated_at: { _gte: $today } }) {
aggregate {
count
}
}
owners_created: owners_aggregate(where: { created_at: { _gte: $today } }) {
aggregate {
count
}
}
owners_updated: owners_aggregate(where: { updated_at: { _gte: $today } }) {
aggregate {
count
}
}
vehicles_created: vehicles_aggregate(
where: { created_at: { _gte: $today } }
) {
aggregate {
count
}
}
vehicles_updated: vehicles_aggregate(
where: { updated_at: { _gte: $today } }
) {
aggregate {
count
}
}
tasks_created: tasks_aggregate(where: { created_at: { _gte: $today } }) {
aggregate {
count
}
}
tasks_updated: tasks_aggregate(where: { updated_at: { _gte: $today } }) {
aggregate {
count
}
}
jobs {
parts_orders_created: parts_orders_aggregate(
where: { created_at: { _gte: $today } }
) {
aggregate {
count
}
}
parts_orders_updated: parts_orders_aggregate(
where: { updated_at: { _gte: $today } }
) {
aggregate {
count
}
}
}
}
}
`

View File

@@ -1,10 +1,11 @@
const express = require("express");
const router = express.Router();
const { autohouse, claimscorp, chatter, kaizen } = require("../data/data");
const { autohouse, claimscorp, chatter, kaizen, usageReport } = require("../data/data");
router.post("/ah", autohouse);
router.post("/cc", claimscorp);
router.post("/chatter", chatter);
router.post("/kaizen", kaizen);
router.post("/usagereport", usageReport);
module.exports = router;

View File

@@ -41,7 +41,7 @@ exports.taskHandler = async (req, res) => {
return res.status(200).send(csv);
} catch (error) {
res.status(500).json({ error: error.message, stack: error.stackTrace });
res.status(500).json({ error: error.message, stack: error.stack });
}
};