Added several fixes throughout for demo.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { UploadOutlined } from "@ant-design/icons";
|
||||
import { Editor } from "@tinymce/tinymce-react";
|
||||
import { Button, Input, Upload, Select } from "antd";
|
||||
import { Button, Card, Input, Select, Upload } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function EmailOverlayComponent({
|
||||
messageOptions,
|
||||
@@ -11,6 +12,7 @@ export default function EmailOverlayComponent({
|
||||
handleUpload,
|
||||
handleFileRemove,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div>
|
||||
To:
|
||||
@@ -53,17 +55,20 @@ export default function EmailOverlayComponent({
|
||||
}}
|
||||
onEditorChange={handleHtmlChange}
|
||||
/>
|
||||
<Upload
|
||||
fileList={messageOptions.fileList}
|
||||
beforeUpload={handleUpload}
|
||||
onRemove={handleFileRemove}
|
||||
multiple
|
||||
listType="picture-card"
|
||||
>
|
||||
<Button>
|
||||
<UploadOutlined /> Upload
|
||||
</Button>
|
||||
</Upload>
|
||||
<Card title={t("emails.labels.attachments")}>
|
||||
<Upload
|
||||
fileList={messageOptions.fileList}
|
||||
beforeUpload={handleUpload}
|
||||
onRemove={handleFileRemove}
|
||||
multiple
|
||||
listType="picture-card"
|
||||
style={{ width: "100%" }}
|
||||
>
|
||||
<Button>
|
||||
<UploadOutlined /> Upload
|
||||
</Button>
|
||||
</Upload>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ export function JobChecklistForm({
|
||||
valuePropName="checked"
|
||||
label={t("checklist.labels.removefromproduction")}
|
||||
>
|
||||
<Switch />
|
||||
<Switch defaultChecked={true} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Collapse, Space } from "antd";
|
||||
import { Collapse, Space, Card } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Gallery from "react-grid-gallery";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -66,13 +66,13 @@ function JobsDocumentsComponent({
|
||||
|
||||
return (
|
||||
<div className="clearfix">
|
||||
<DocumentsUploadComponent
|
||||
jobId={jobId}
|
||||
billId={billId}
|
||||
callbackAfterUpload={billsCallback || refetch}
|
||||
tagsArray={["test"]}
|
||||
/>
|
||||
<Space>
|
||||
<DocumentsUploadComponent
|
||||
jobId={jobId}
|
||||
billId={billId}
|
||||
callbackAfterUpload={billsCallback || refetch}
|
||||
tagsArray={["test"]}
|
||||
/>
|
||||
<JobsDocumentsGallerySelectAllComponent
|
||||
galleryImages={galleryImages}
|
||||
setGalleryImages={setgalleryImages}
|
||||
@@ -83,12 +83,8 @@ function JobsDocumentsComponent({
|
||||
deletionCallback={billsCallback || refetch}
|
||||
/>
|
||||
</Space>
|
||||
<Collapse
|
||||
style={{ marginTop: "2rem" }}
|
||||
defaultActiveKey={["images", "other"]}
|
||||
bordered="false"
|
||||
>
|
||||
<Collapse.Panel key="images" header={t("jobs.labels.documents-images")}>
|
||||
<div style={{ marginTop: "2rem" }}>
|
||||
<Card title={t("jobs.labels.documents-images")}>
|
||||
<Gallery
|
||||
images={galleryImages.images}
|
||||
backdropClosesModal={true}
|
||||
@@ -108,8 +104,9 @@ function JobsDocumentsComponent({
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel key="other" header={t("jobs.labels.documents-other")}>
|
||||
</Card>
|
||||
|
||||
<Card title={t("jobs.labels.documents-other")}>
|
||||
<Gallery
|
||||
images={galleryImages.other}
|
||||
backdropClosesModal={true}
|
||||
@@ -130,8 +127,8 @@ function JobsDocumentsComponent({
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Collapse.Panel>
|
||||
</Collapse>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -150,6 +150,12 @@ export function JobsList({ bodyshop }) {
|
||||
) : null;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.ins_co_nm"),
|
||||
dataIndex: "ins_co_nm",
|
||||
key: "ins_co_nm",
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: t("jobs.fields.status"),
|
||||
dataIndex: "status",
|
||||
|
||||
@@ -75,9 +75,13 @@ export const createBoardData = (AllStatuses, Jobs, filter) => {
|
||||
const DataGroupedByStatus = _.groupBy(filteredJobs, (d) => d.status);
|
||||
|
||||
Object.keys(DataGroupedByStatus).map((statusGroupKey) => {
|
||||
boardLanes.columns.find(
|
||||
(l) => l.id === statusGroupKey
|
||||
).cards = sortByParentId(DataGroupedByStatus[statusGroupKey]);
|
||||
try {
|
||||
boardLanes.columns.find(
|
||||
(l) => l.id === statusGroupKey
|
||||
).cards = sortByParentId(DataGroupedByStatus[statusGroupKey]);
|
||||
} catch (error) {
|
||||
console.log("Error while creating board card", error);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ import ProductionListColumnNote from "./production-list-columns.productionnote.c
|
||||
import ProductionListColumnStatus from "./production-list-columns.status.component";
|
||||
import ProductionlistColumnTouchTime from "./prodution-list-columns.touchtime.component";
|
||||
|
||||
const OneCalendarDay = 60 * 60 * 24 * 1000;
|
||||
const Now = new Date();
|
||||
|
||||
const r = [
|
||||
{
|
||||
title: i18n.t("jobs.actions.viewdetail"),
|
||||
@@ -75,7 +78,16 @@ const r = [
|
||||
ellipsis: true,
|
||||
sorter: (a, b) => a.scheduled_completion - b.scheduled_completion,
|
||||
render: (text, record) => (
|
||||
<DateFormatter>{record.scheduled_completion}</DateFormatter>
|
||||
<DateFormatter
|
||||
className={
|
||||
!!record.scheduled_completion &&
|
||||
new Date(record.scheduled_completion) - Now < OneCalendarDay
|
||||
? "production-completion-1"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
{record.scheduled_completion}
|
||||
</DateFormatter>
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -156,13 +168,17 @@ const r = [
|
||||
title: i18n.t("production.labels.bodyhours"),
|
||||
dataIndex: "labhrs",
|
||||
key: "labhrs",
|
||||
sorter: (a, b) => a.labhrs - b.labhrs,
|
||||
sorter: (a, b) =>
|
||||
a.labhrs.aggregate.sum.mod_lb_hrs - b.labhrs.aggregate.sum.mod_lb_hrs,
|
||||
render: (text, record) => record.labhrs.aggregate.sum.mod_lb_hrs,
|
||||
},
|
||||
{
|
||||
title: i18n.t("production.labels.refinishhours"),
|
||||
dataIndex: "larhrs",
|
||||
key: "larhrs",
|
||||
sorter: (a, b) => a.larhrs - b.larhrs,
|
||||
sorter: (a, b) =>
|
||||
a.larhrs.aggregate.sum.mod_lb_hrs - b.larhrs.aggregate.sum.mod_lb_hrs,
|
||||
render: (text, record) => record.larhrs.aggregate.sum.mod_lb_hrs,
|
||||
},
|
||||
{
|
||||
title: i18n.t("production.labels.alert"),
|
||||
|
||||
@@ -16,8 +16,6 @@ const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const OneCalendarDay = 60 * 60 * 24 * 1000;
|
||||
|
||||
export function ProductionListTable({
|
||||
columnState,
|
||||
loading,
|
||||
@@ -174,12 +172,12 @@ export function ProductionListTable({
|
||||
dataSource={dataSource}
|
||||
onChange={handleTableChange}
|
||||
rowClassName={(record, index) => {
|
||||
const classes = []; //TODO What could be good usage here?
|
||||
if (!!record.scheduled_completion) {
|
||||
if (new Date(record.scheduled_completion) - Now < OneCalendarDay)
|
||||
classes.push("production-completion-1");
|
||||
}
|
||||
return classes.join(" ");
|
||||
// const classes = []; //TODO What could be good usage here?
|
||||
// if (!!record.scheduled_completion) {
|
||||
// if (new Date(record.scheduled_completion) - Now < OneCalendarDay)
|
||||
// classes.push("production-completion-1");
|
||||
// }
|
||||
// return classes.join(" ");
|
||||
}}
|
||||
/>
|
||||
</ReactDragListView.DragColumn>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button, Col, Form, Row, Select, Switch } from "antd";
|
||||
import { Button, Col, Form, Row, Select, Switch, Card } from "antd";
|
||||
import axios from "axios";
|
||||
import moment from "moment";
|
||||
import React, { useState } from "react";
|
||||
@@ -79,9 +79,26 @@ export function ScheduleJobModalComponent({
|
||||
>
|
||||
<DateTimePicker onBlur={handleDateBlur} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="scheduled_completion"
|
||||
label={t("jobs.fields.scheduled_completion")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<Card title={t("appointments.labels.smartscheduling")}>
|
||||
<Button onClick={handleAuto} loading={loading}>
|
||||
{t("appointments.actions.smartscheduling")}
|
||||
{t("appointments.actions.calculate")}
|
||||
</Button>
|
||||
{smartOptions.length > 0 && (
|
||||
<div>{t("appointments.labels.suggesteddates")}</div>
|
||||
)}
|
||||
<div className="imex-flex-row imex-flex-row__flex-space-around">
|
||||
{smartOptions.map((d, idx) => (
|
||||
<Button
|
||||
@@ -96,20 +113,8 @@ export function ScheduleJobModalComponent({
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</LayoutFormRow>
|
||||
</Card>
|
||||
<LayoutFormRow grow>
|
||||
<Form.Item
|
||||
name="scheduled_completion"
|
||||
label={t("jobs.fields.scheduled_completion")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<DateTimePicker />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="notifyCustomer"
|
||||
valuePropName="checked"
|
||||
@@ -120,6 +125,8 @@ export function ScheduleJobModalComponent({
|
||||
<Form.Item name="email" label={t("jobs.fields.ownr_ea")}>
|
||||
<EmailInput />
|
||||
</Form.Item>
|
||||
</LayoutFormRow>
|
||||
<LayoutFormRow grow>
|
||||
<Form.Item name="color" label={t("appointments.fields.color")}>
|
||||
<Select>
|
||||
{bodyshop.appt_colors &&
|
||||
|
||||
@@ -128,7 +128,7 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
|
||||
]}
|
||||
>
|
||||
<Select mode="multiple">
|
||||
{Object.keys(TemplateList).map((i) => (
|
||||
{Object.keys(TemplateList()).map((i) => (
|
||||
<Select.Option
|
||||
key={TemplateList()[i].key}
|
||||
value={TemplateList()[i].key}
|
||||
@@ -250,7 +250,7 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
|
||||
]}
|
||||
>
|
||||
<Select mode="multiple">
|
||||
{Object.keys(TemplateList).map((i) => (
|
||||
{Object.keys(TemplateList()).map((i) => (
|
||||
<Select.Option
|
||||
key={TemplateList()[i].key}
|
||||
value={TemplateList()[i].key}
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
"appointments": {
|
||||
"actions": {
|
||||
"block": "Block Day",
|
||||
"calculate": "Calculate SMART Dates",
|
||||
"cancel": "Cancel",
|
||||
"intake": "Intake",
|
||||
"new": "New Appointment",
|
||||
"reschedule": "Reschedule",
|
||||
"smartscheduling": "SMART Scheduling",
|
||||
"viewjob": "View Job"
|
||||
},
|
||||
"errors": {
|
||||
@@ -33,8 +33,8 @@
|
||||
"saving": "Error scheduling appointment. {{message}}"
|
||||
},
|
||||
"fields": {
|
||||
"alt_transport": "A.T.",
|
||||
"color": "Color",
|
||||
"alt_transport": "Alt. Trans.",
|
||||
"color": "Appointment Color",
|
||||
"time": "Appointment Time",
|
||||
"title": "Title"
|
||||
},
|
||||
@@ -50,7 +50,9 @@
|
||||
"nocompletingjobs": "No jobs scheduled for completion.",
|
||||
"nodateselected": "No date has been selected.",
|
||||
"priorappointments": "Previous Appointments",
|
||||
"scheduledfor": "Scheduled appointment for: "
|
||||
"scheduledfor": "Scheduled appointment for: ",
|
||||
"smartscheduling": "Smart Scheduling",
|
||||
"suggesteddates": "Suggested Dates"
|
||||
},
|
||||
"successes": {
|
||||
"canceled": "Appointment canceled successfully.",
|
||||
@@ -182,11 +184,17 @@
|
||||
"country": "Country",
|
||||
"dailybodytarget": "Scoreboard - Daily Body Target",
|
||||
"dailypainttarget": "Scoreboard - Daily Paint Target",
|
||||
"deliver": {
|
||||
"templates": "Delivery Templates"
|
||||
},
|
||||
"email": "General Shop Email",
|
||||
"enforce_class": "Enforce Class on Conversion?",
|
||||
"federal_tax_id": "Federal Tax ID (GST/HST)",
|
||||
"inhousevendorid": "In House Vendor ID",
|
||||
"insurance_vendor_id": "Insurance Vendor ID",
|
||||
"intake": {
|
||||
"template": "Intake Templates"
|
||||
},
|
||||
"invoice_federal_tax_rate": "Invoices - Federal Tax Rate",
|
||||
"invoice_local_tax_rate": "Invoices - Local Tax Rate",
|
||||
"invoice_state_tax_rate": "Invoices - State Tax Rate",
|
||||
@@ -583,6 +591,9 @@
|
||||
"errors": {
|
||||
"notsent": "Email not sent. Error encountered while sending {{message}}"
|
||||
},
|
||||
"labels": {
|
||||
"attachments": "Attachments"
|
||||
},
|
||||
"successes": {
|
||||
"sent": "Email sent successfully."
|
||||
}
|
||||
@@ -713,6 +724,11 @@
|
||||
"rescuetitle": "Rescue Me!"
|
||||
}
|
||||
},
|
||||
"intake": {
|
||||
"labels": {
|
||||
"printpack": "Intake Print Pack"
|
||||
}
|
||||
},
|
||||
"joblines": {
|
||||
"actions": {
|
||||
"new": "New Line"
|
||||
@@ -1344,15 +1360,21 @@
|
||||
},
|
||||
"jobs": {
|
||||
"all_job_notes": "All Job Notes",
|
||||
"appointment_confirmation": "Appointment Confirmation",
|
||||
"appointment_reminder": "Appointment Reminder",
|
||||
"casl_authorization": "CASL Authorization",
|
||||
"casl_work_authorization": "CASL Work Authorization",
|
||||
"cover_sheet": "Cover Sheet",
|
||||
"coversheet": "Coversheet",
|
||||
"csi_invitation": "CSI Invite",
|
||||
"estimate_detail": "Estimate Details",
|
||||
"fippa_authorization": "FIPPA Authorization",
|
||||
"fippa_work_authorization": "FIPPA Work Authorization",
|
||||
"job_totals": "Job Totals Only",
|
||||
"parts_order_confirmation": "Parts Order Confirmation",
|
||||
"parts_return_confirmation": "Parts Return Confirmation",
|
||||
"payment_recept": "Payment Receipt",
|
||||
"time_tickets_by_employee": "Time Tickets By Employee",
|
||||
"window_tag": "Window Tag",
|
||||
"work_authorization": "Work Authorization"
|
||||
},
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
"appointments": {
|
||||
"actions": {
|
||||
"block": "",
|
||||
"calculate": "",
|
||||
"cancel": "Cancelar",
|
||||
"intake": "Consumo",
|
||||
"new": "Nueva cita",
|
||||
"reschedule": "Reprogramar",
|
||||
"smartscheduling": "",
|
||||
"viewjob": "Ver trabajo"
|
||||
},
|
||||
"errors": {
|
||||
@@ -50,7 +50,9 @@
|
||||
"nocompletingjobs": "",
|
||||
"nodateselected": "No se ha seleccionado ninguna fecha.",
|
||||
"priorappointments": "Nombramientos previos",
|
||||
"scheduledfor": "Cita programada para:"
|
||||
"scheduledfor": "Cita programada para:",
|
||||
"smartscheduling": "",
|
||||
"suggesteddates": ""
|
||||
},
|
||||
"successes": {
|
||||
"canceled": "Cita cancelada con éxito.",
|
||||
@@ -182,11 +184,17 @@
|
||||
"country": "",
|
||||
"dailybodytarget": "",
|
||||
"dailypainttarget": "",
|
||||
"deliver": {
|
||||
"templates": ""
|
||||
},
|
||||
"email": "",
|
||||
"enforce_class": "",
|
||||
"federal_tax_id": "",
|
||||
"inhousevendorid": "",
|
||||
"insurance_vendor_id": "",
|
||||
"intake": {
|
||||
"template": ""
|
||||
},
|
||||
"invoice_federal_tax_rate": "",
|
||||
"invoice_local_tax_rate": "",
|
||||
"invoice_state_tax_rate": "",
|
||||
@@ -583,6 +591,9 @@
|
||||
"errors": {
|
||||
"notsent": "Correo electrónico no enviado Se encontró un error al enviar {{message}}"
|
||||
},
|
||||
"labels": {
|
||||
"attachments": ""
|
||||
},
|
||||
"successes": {
|
||||
"sent": "Correo electrónico enviado con éxito."
|
||||
}
|
||||
@@ -713,6 +724,11 @@
|
||||
"rescuetitle": ""
|
||||
}
|
||||
},
|
||||
"intake": {
|
||||
"labels": {
|
||||
"printpack": ""
|
||||
}
|
||||
},
|
||||
"joblines": {
|
||||
"actions": {
|
||||
"new": ""
|
||||
@@ -1344,15 +1360,21 @@
|
||||
},
|
||||
"jobs": {
|
||||
"all_job_notes": "",
|
||||
"appointment_confirmation": "",
|
||||
"appointment_reminder": "",
|
||||
"casl_authorization": "",
|
||||
"casl_work_authorization": "",
|
||||
"cover_sheet": "",
|
||||
"coversheet": "",
|
||||
"csi_invitation": "",
|
||||
"estimate_detail": "",
|
||||
"fippa_authorization": "",
|
||||
"fippa_work_authorization": "",
|
||||
"job_totals": "",
|
||||
"parts_order_confirmation": "",
|
||||
"parts_return_confirmation": "",
|
||||
"payment_recept": "",
|
||||
"time_tickets_by_employee": "",
|
||||
"window_tag": "",
|
||||
"work_authorization": ""
|
||||
},
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
"appointments": {
|
||||
"actions": {
|
||||
"block": "",
|
||||
"calculate": "",
|
||||
"cancel": "annuler",
|
||||
"intake": "Admission",
|
||||
"new": "Nouveau rendez-vous",
|
||||
"reschedule": "Replanifier",
|
||||
"smartscheduling": "",
|
||||
"viewjob": "Voir le travail"
|
||||
},
|
||||
"errors": {
|
||||
@@ -50,7 +50,9 @@
|
||||
"nocompletingjobs": "",
|
||||
"nodateselected": "Aucune date n'a été sélectionnée.",
|
||||
"priorappointments": "Rendez-vous précédents",
|
||||
"scheduledfor": "Rendez-vous prévu pour:"
|
||||
"scheduledfor": "Rendez-vous prévu pour:",
|
||||
"smartscheduling": "",
|
||||
"suggesteddates": ""
|
||||
},
|
||||
"successes": {
|
||||
"canceled": "Rendez-vous annulé avec succès.",
|
||||
@@ -182,11 +184,17 @@
|
||||
"country": "",
|
||||
"dailybodytarget": "",
|
||||
"dailypainttarget": "",
|
||||
"deliver": {
|
||||
"templates": ""
|
||||
},
|
||||
"email": "",
|
||||
"enforce_class": "",
|
||||
"federal_tax_id": "",
|
||||
"inhousevendorid": "",
|
||||
"insurance_vendor_id": "",
|
||||
"intake": {
|
||||
"template": ""
|
||||
},
|
||||
"invoice_federal_tax_rate": "",
|
||||
"invoice_local_tax_rate": "",
|
||||
"invoice_state_tax_rate": "",
|
||||
@@ -583,6 +591,9 @@
|
||||
"errors": {
|
||||
"notsent": "Courriel non envoyé. Erreur rencontrée lors de l'envoi de {{message}}"
|
||||
},
|
||||
"labels": {
|
||||
"attachments": ""
|
||||
},
|
||||
"successes": {
|
||||
"sent": "E-mail envoyé avec succès."
|
||||
}
|
||||
@@ -713,6 +724,11 @@
|
||||
"rescuetitle": ""
|
||||
}
|
||||
},
|
||||
"intake": {
|
||||
"labels": {
|
||||
"printpack": ""
|
||||
}
|
||||
},
|
||||
"joblines": {
|
||||
"actions": {
|
||||
"new": ""
|
||||
@@ -1344,15 +1360,21 @@
|
||||
},
|
||||
"jobs": {
|
||||
"all_job_notes": "",
|
||||
"appointment_confirmation": "",
|
||||
"appointment_reminder": "",
|
||||
"casl_authorization": "",
|
||||
"casl_work_authorization": "",
|
||||
"cover_sheet": "",
|
||||
"coversheet": "",
|
||||
"csi_invitation": "",
|
||||
"estimate_detail": "",
|
||||
"fippa_authorization": "",
|
||||
"fippa_work_authorization": "",
|
||||
"job_totals": "",
|
||||
"parts_order_confirmation": "",
|
||||
"parts_return_confirmation": "",
|
||||
"payment_recept": "",
|
||||
"time_tickets_by_employee": "",
|
||||
"window_tag": "",
|
||||
"work_authorization": ""
|
||||
},
|
||||
|
||||
@@ -5,7 +5,9 @@ import { Tooltip } from "antd";
|
||||
|
||||
export function DateFormatter(props) {
|
||||
return props.children ? (
|
||||
<Moment format="MM/DD/YYYY ">{props.children}</Moment>
|
||||
<Moment className={props && props.className} format="MM/DD/YYYY ">
|
||||
{props.children}
|
||||
</Moment>
|
||||
) : null;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ export const TemplateList = (type, object) => {
|
||||
"Sent to a customer as a reminder of an upcoming appointment.",
|
||||
drivingId: "appointment",
|
||||
key: "appointment_reminder",
|
||||
subject: `Appointment Reminder`,
|
||||
},
|
||||
appointment_confirmation: {
|
||||
title: "Appointment Confirmation",
|
||||
|
||||
Reference in New Issue
Block a user