@@ -115,6 +118,9 @@ export function DCForm({
placeholder="Select AP Account"
description="The AP Account associated with the Division Code"
isClearable
+ hasPopoutWindow
+ popoutLink="/accounting/gl-accounts"
+ popoutLinkLabel="GL Account"
/>
@@ -129,6 +135,9 @@ export function DCForm({
placeholder="Select Expense Account"
description="The Expense Account associated with the Revenue Code"
isClearable
+ hasPopoutWindow
+ popoutLink="/accounting/gl-accounts"
+ popoutLinkLabel="GL Account"
/>
diff --git a/client/src/components/document-class/document-class-table-dialog.tsx b/client/src/components/document-class/document-class-table-dialog.tsx
new file mode 100644
index 000000000..76456d89a
--- /dev/null
+++ b/client/src/components/document-class/document-class-table-dialog.tsx
@@ -0,0 +1,130 @@
+/*
+ * COPYRIGHT(c) 2023 MONTA
+ *
+ * This file is part of Monta.
+ *
+ * The Monta software is licensed under the Business Source License 1.1. You are granted the right
+ * to copy, modify, and redistribute the software, but only for non-production use or with a total
+ * of less than three server instances. Starting from the Change Date (November 16, 2026), the
+ * software will be made available under version 2 or later of the GNU General Public License.
+ * If you use the software in violation of this license, your rights under the license will be
+ * terminated automatically. The software is provided "as is," and the Licensor disclaims all
+ * warranties and conditions. If you use this license's text or the "Business Source License" name
+ * and trademark, you must comply with the Licensor's covenants, which include specifying the
+ * Change License as the GPL Version 2.0 or a compatible license, specifying an Additional Use
+ * Grant, and not modifying the license in any other way.
+ */
+
+import { InputField } from "@/components/common/fields/input";
+import { TextareaField } from "@/components/common/fields/textarea";
+import { Button } from "@/components/ui/button";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog";
+import { Form, FormControl, FormGroup } from "@/components/ui/form";
+import { useCustomMutation } from "@/hooks/useCustomMutation";
+import { documentClassSchema } from "@/lib/validations/BillingSchema";
+import { DocumentClassificationFormValues as FormValues } from "@/types/billing";
+import { TableSheetProps } from "@/types/tables";
+import { yupResolver } from "@hookform/resolvers/yup";
+import React from "react";
+import { Control, useForm } from "react-hook-form";
+
+export function DocumentClassForm({
+ control,
+}: {
+ control: Control;
+}) {
+ return (
+
+ );
+}
+
+export function DocumentClassDialog({ onOpenChange, open }: TableSheetProps) {
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
+
+ const { control, reset, handleSubmit } = useForm({
+ resolver: yupResolver(documentClassSchema),
+ defaultValues: {
+ name: "",
+ description: "",
+ },
+ });
+
+ const mutation = useCustomMutation(
+ control,
+ {
+ method: "POST",
+ path: "/document_classifications/",
+ successMessage: "Document Classification created successfully.",
+ queryKeysToInvalidate: ["document-classification-table-data"],
+ closeModal: true,
+ errorMessage: "Failed to create new document classification.",
+ },
+ () => setIsSubmitting(false),
+ reset,
+ );
+
+ const onSubmit = (values: FormValues) => {
+ setIsSubmitting(true);
+ mutation.mutate(values);
+ };
+
+ return (
+
+ );
+}
diff --git a/client/src/components/document-class/document-class-table-edit-dialog.tsx b/client/src/components/document-class/document-class-table-edit-dialog.tsx
new file mode 100644
index 000000000..aff90f61f
--- /dev/null
+++ b/client/src/components/document-class/document-class-table-edit-dialog.tsx
@@ -0,0 +1,113 @@
+/*
+ * COPYRIGHT(c) 2023 MONTA
+ *
+ * This file is part of Monta.
+ *
+ * The Monta software is licensed under the Business Source License 1.1. You are granted the right
+ * to copy, modify, and redistribute the software, but only for non-production use or with a total
+ * of less than three server instances. Starting from the Change Date (November 16, 2026), the
+ * software will be made available under version 2 or later of the GNU General Public License.
+ * If you use the software in violation of this license, your rights under the license will be
+ * terminated automatically. The software is provided "as is," and the Licensor disclaims all
+ * warranties and conditions. If you use this license's text or the "Business Source License" name
+ * and trademark, you must comply with the Licensor's covenants, which include specifying the
+ * Change License as the GPL Version 2.0 or a compatible license, specifying an Additional Use
+ * Grant, and not modifying the license in any other way.
+ */
+
+import { useCustomMutation } from "@/hooks/useCustomMutation";
+import { formatDate } from "@/lib/date";
+import { documentClassSchema } from "@/lib/validations/BillingSchema";
+import { useTableStore } from "@/stores/TableStore";
+import {
+ DocumentClassification,
+ DocumentClassificationFormValues as FormValues,
+} from "@/types/billing";
+import { TableSheetProps } from "@/types/tables";
+import { yupResolver } from "@hookform/resolvers/yup";
+import React from "react";
+import { useForm } from "react-hook-form";
+import { Button } from "../ui/button";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "../ui/dialog";
+import { DocumentClassForm } from "@/components/document-class/document-class-table-dialog";
+
+function DocumentClassEditForm({
+ documentClass,
+}: {
+ documentClass: DocumentClassification;
+}) {
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
+ const { control, reset, handleSubmit } = useForm({
+ resolver: yupResolver(documentClassSchema),
+ defaultValues: {
+ name: documentClass.name,
+ description: documentClass.description,
+ },
+ });
+
+ const mutation = useCustomMutation(
+ control,
+ {
+ method: "PUT",
+ path: `/document_classifications/${documentClass.id}/`,
+ successMessage: "Document Classification updated successfully.",
+ queryKeysToInvalidate: ["document-classification-table-data"],
+ closeModal: true,
+ errorMessage: "Failed to update document classification.",
+ },
+ () => setIsSubmitting(false),
+ reset,
+ );
+
+ const onSubmit = (values: FormValues) => {
+ setIsSubmitting(true);
+ mutation.mutate(values);
+ };
+
+ return (
+
+ );
+}
+
+export function DocumentClassEditDialog({
+ onOpenChange,
+ open,
+}: TableSheetProps) {
+ const [documentClass] = useTableStore.use(
+ "currentRecord",
+ ) as DocumentClassification[];
+
+ return (
+
+ );
+}
diff --git a/client/src/components/ui/form.tsx b/client/src/components/ui/form.tsx
index 1e663a130..d218b4b0c 100644
--- a/client/src/components/ui/form.tsx
+++ b/client/src/components/ui/form.tsx
@@ -1,3 +1,19 @@
+/*
+ * COPYRIGHT(c) 2023 MONTA
+ *
+ * This file is part of Monta.
+ *
+ * The Monta software is licensed under the Business Source License 1.1. You are granted the right
+ * to copy, modify, and redistribute the software, but only for non-production use or with a total
+ * of less than three server instances. Starting from the Change Date (November 16, 2026), the
+ * software will be made available under version 2 or later of the GNU General Public License.
+ * If you use the software in violation of this license, your rights under the license will be
+ * terminated automatically. The software is provided "as is," and the Licensor disclaims all
+ * warranties and conditions. If you use this license's text or the "Business Source License" name
+ * and trademark, you must comply with the Licensor's covenants, which include specifying the
+ * Change License as the GPL Version 2.0 or a compatible license, specifying an Additional Use
+ * Grant, and not modifying the license in any other way.
+ */
import * as React from "react";
import { cn } from "@/lib/utils";
@@ -36,7 +52,7 @@ export const FormControl = React.forwardRef<
React.HTMLAttributes
>(({ className, ...props }, ref) => {
return (
-
+
);
diff --git a/client/src/lib/validations/BillingSchema.ts b/client/src/lib/validations/BillingSchema.ts
index 3e9305fef..864694505 100644
--- a/client/src/lib/validations/BillingSchema.ts
+++ b/client/src/lib/validations/BillingSchema.ts
@@ -21,6 +21,7 @@ import {
AccessorialChargeFormValues,
BillingControlFormValues,
ChargeTypeFormValues,
+ DocumentClassificationFormValues,
} from "@/types/billing";
import {
AutoBillingCriteriaChoicesProps,
@@ -64,6 +65,16 @@ export const chargeTypeSchema: ObjectSchema =
.notRequired(),
});
+export const documentClassSchema: ObjectSchema =
+ Yup.object().shape({
+ name: Yup.string()
+ .max(10, "Name must be less than 10 characters.")
+ .required("Name is required"),
+ description: Yup.string()
+ .max(100, "Description must be less than 100 characters.")
+ .notRequired(),
+ });
+
export const billingControlSchema: ObjectSchema =
Yup.object().shape({
removeBillingHistory: Yup.boolean().required(
diff --git a/client/src/pages/billing/DocumentClassification.tsx b/client/src/pages/billing/DocumentClassification.tsx
new file mode 100644
index 000000000..42a15e28e
--- /dev/null
+++ b/client/src/pages/billing/DocumentClassification.tsx
@@ -0,0 +1,74 @@
+/*
+ * COPYRIGHT(c) 2023 MONTA
+ *
+ * This file is part of Monta.
+ *
+ * The Monta software is licensed under the Business Source License 1.1. You are granted the right
+ * to copy, modify, and redistribute the software, but only for non-production use or with a total
+ * of less than three server instances. Starting from the Change Date (November 16, 2026), the
+ * software will be made available under version 2 or later of the GNU General Public License.
+ * If you use the software in violation of this license, your rights under the license will be
+ * terminated automatically. The software is provided "as is," and the Licensor disclaims all
+ * warranties and conditions. If you use this license's text or the "Business Source License" name
+ * and trademark, you must comply with the Licensor's covenants, which include specifying the
+ * Change License as the GPL Version 2.0 or a compatible license, specifying an Additional Use
+ * Grant, and not modifying the license in any other way.
+ */
+
+import { Checkbox } from "@/components/common/fields/checkbox";
+import { DataTable } from "@/components/common/table/data-table";
+import { DataTableColumnHeader } from "@/components/common/table/data-table-column-header";
+import { DocumentClassDialog } from "@/components/document-class/document-class-table-dialog";
+import { DocumentClassification } from "@/types/billing";
+import { ColumnDef } from "@tanstack/react-table";
+import { DocumentClassEditDialog } from "@/components/document-class/document-class-table-edit-dialog";
+
+const columns: ColumnDef[] = [
+ {
+ id: "select",
+ header: ({ table }) => (
+ table.toggleAllPageRowsSelected(!!value)}
+ aria-label="Select all"
+ className="translate-y-[2px]"
+ />
+ ),
+ cell: ({ row }) => (
+ row.toggleSelected(!!value)}
+ aria-label="Select row"
+ className="translate-y-[2px]"
+ />
+ ),
+ enableSorting: false,
+ enableHiding: false,
+ },
+ {
+ accessorKey: "name",
+ header: ({ column }) => (
+
+ ),
+ },
+ {
+ accessorKey: "description",
+ header: "Description",
+ },
+];
+
+export default function DocumentClassificationPage() {
+ return (
+
+ );
+}
diff --git a/client/src/routing/AppRoutes.tsx b/client/src/routing/AppRoutes.tsx
index a334b3404..f7ba38521 100644
--- a/client/src/routing/AppRoutes.tsx
+++ b/client/src/routing/AppRoutes.tsx
@@ -65,6 +65,9 @@ const ShipmentTypePage = lazy(() => import("../pages/shipment/ShipmentType"));
const LocationPage = lazy(() => import("../pages/dispatch/Location"));
const TrailerPage = lazy(() => import("../pages/equipment/Trailer"));
const TractorPage = lazy(() => import("../pages/equipment/Tractor"));
+const DocumentClassPage = lazy(
+ () => import("../pages/billing/DocumentClassification"),
+);
const AdminPage = lazy(() => import("../pages/admin/Dashboard"));
export type RouteObjectWithPermission = RouteObject & {
@@ -175,6 +178,15 @@ export const routes: RouteObjectWithPermission[] = [
element: ,
permission: "view_chargetype",
},
+ {
+ title: "Document Classifications",
+ group: "billing",
+ subMenu: "configuration files",
+ path: "/billing/document-classes",
+ description: "Manage document classifications",
+ element: ,
+ permission: "view_documentclassification",
+ },
{
title: "Accessorial Charges",
group: "billing",
diff --git a/client/src/types/billing.ts b/client/src/types/billing.ts
index da98a462e..0a9522209 100644
--- a/client/src/types/billing.ts
+++ b/client/src/types/billing.ts
@@ -15,13 +15,13 @@
* Grant, and not modifying the license in any other way.
*/
+import { StatusChoiceProps } from "@/types/index";
+import { BaseModel } from "@/types/organization";
import {
AutoBillingCriteriaChoicesProps,
FuelMethodChoicesProps,
OrderTransferCriteriaChoicesProps,
} from "@/utils/apps/billing";
-import { BaseModel } from "@/types/organization";
-import { StatusChoiceProps } from "@/types/index";
/** Types for Billing Control */
export interface BillingControl extends BaseModel {
@@ -140,8 +140,14 @@ export interface BillingHistory extends BaseModel {
}
/** Types for Document Classification */
-export type DocumentClassification = {
+export interface DocumentClassification extends BaseModel {
id: string;
name: string;
- description: string;
-};
+ description?: string | null;
+}
+
+/** Types for Document Classification */
+export type DocumentClassificationFormValues = Omit<
+ DocumentClassification,
+ "id" | "organization" | "created" | "modified"
+>;
diff --git a/client/src/types/index.ts b/client/src/types/index.ts
index 97bf53e5f..2b0275e27 100644
--- a/client/src/types/index.ts
+++ b/client/src/types/index.ts
@@ -53,6 +53,7 @@ export type QueryKeys =
| "delayCodes"
| "division-code-table-data"
| "divisionCodes"
+ | "document-classification-table-data"
| "documentClassifications"
| "depots"
| "emailControl"
diff --git a/server/billing/api.py b/server/billing/api.py
index 1a5b41870..c3e5ece24 100644
--- a/server/billing/api.py
+++ b/server/billing/api.py
@@ -322,8 +322,7 @@ def get_queryset(self) -> QuerySet[models.AccessorialCharge]:
class DocumentClassificationViewSet(viewsets.ModelViewSet):
- """
- A viewset for viewing and editing document classifications in the system.
+ """A viewset for viewing and editing document classifications in the system.
The viewset provides default operations for creating, updating, and
deleting document classifications, as well as listing and retrieving document classifications.
@@ -342,12 +341,7 @@ class to convert the document classification instances to and from JSON-formatte
def get_queryset(self) -> QuerySet[models.DocumentClassification]:
queryset = self.queryset.filter(
organization_id=self.request.user.organization_id # type: ignore
- ).only(
- "id",
- "name",
- "organization_id",
- "description",
- )
+ ).all()
return queryset
diff --git a/server/billing/models.py b/server/billing/models.py
index fcb9549bb..11cbaeffd 100644
--- a/server/billing/models.py
+++ b/server/billing/models.py
@@ -436,7 +436,7 @@ class DocumentClassification(GenericModel):
)
name = models.CharField(
_("Name"),
- max_length=150,
+ max_length=10,
help_text=_("Document classification name"),
)
description = models.TextField(
@@ -452,7 +452,6 @@ class Meta:
verbose_name = _("Document Classification")
verbose_name_plural = _("Document Classifications")
- ordering = ["name"]
db_table = "document_classification"
db_table_comment = (
"Stores document classification information for related organization."
diff --git a/server/reports/helpers.py b/server/reports/helpers.py
index 3c78236c0..28a94e466 100644
--- a/server/reports/helpers.py
+++ b/server/reports/helpers.py
@@ -372,4 +372,14 @@
{"value": "modified", "label": "Modified"},
],
},
+ "DocumentClassification": {
+ "app_label": "billing",
+ "allowed_fields": [
+ {"value": "organization__name", "label": "Organization Name"},
+ {"value": "name", "label": "Name"},
+ {"value": "description", "label": "Description"},
+ {"value": "created", "label": "Created"},
+ {"value": "modified", "label": "Modified"},
+ ],
+ },
}
From 572d497fc8c4adea7d7cdf13cb4f8e2745ceb234 Mon Sep 17 00:00:00 2001
From: Eric Moss
Date: Tue, 19 Dec 2023 19:48:43 -0500
Subject: [PATCH 02/14] add: new props to react-select
---
client/src/types/react-select-extension.d.ts | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/client/src/types/react-select-extension.d.ts b/client/src/types/react-select-extension.d.ts
index acdfb9a2a..6d49796a9 100644
--- a/client/src/types/react-select-extension.d.ts
+++ b/client/src/types/react-select-extension.d.ts
@@ -16,7 +16,6 @@
*/
import { Options } from "react-select"; // eslint-disable-line import/no-unassigned-import
-import type { } from "react-select/base"; // eslint-disable-line import/no-unassigned-import
type _ = Options; // Added just so auto sort import won't remove the import
@@ -26,42 +25,43 @@ declare module "react-select/base" {
IsMulti extends boolean,
Group extends GroupBase
diff --git a/client/src/components/customer/customer-rule-profile-form.tsx b/client/src/components/customer/customer-rule-profile-form.tsx
index 94620b07c..f07923b41 100644
--- a/client/src/components/customer/customer-rule-profile-form.tsx
+++ b/client/src/components/customer/customer-rule-profile-form.tsx
@@ -66,6 +66,9 @@ export function CustomerRuleProfileForm({
isLoading={isDocumentClassLoading}
placeholder="Select Document Classification"
description="Select the state or region for the customer."
+ hasPopoutWindow
+ popoutLink="#" // TODO: Change once Document Classification is added.
+ popoutLinkLabel="Document Classification"
/>
diff --git a/client/src/components/customer/delivery-slots-form.tsx b/client/src/components/customer/delivery-slots-form.tsx
index 2d25a68ad..c58fd32f8 100644
--- a/client/src/components/customer/delivery-slots-form.tsx
+++ b/client/src/components/customer/delivery-slots-form.tsx
@@ -106,6 +106,9 @@ export function DeliverySlotForm({
isClearable={false}
menuPlacement="bottom"
menuPosition="fixed"
+ hasPopoutWindow
+ popoutLink="/dispatch/locations/" // TODO: Change once Document Classification is added.
+ popoutLinkLabel="Location"
/>
diff --git a/client/src/components/fleet-codes/fleet-code-table-dialog.tsx b/client/src/components/fleet-codes/fleet-code-table-dialog.tsx
index 166200654..45fb1bd36 100644
--- a/client/src/components/fleet-codes/fleet-code-table-dialog.tsx
+++ b/client/src/components/fleet-codes/fleet-code-table-dialog.tsx
@@ -131,6 +131,9 @@ export function FleetCodeForm({
placeholder="Select Manager"
description="User who manages the Fleet Code"
isClearable
+ hasPopoutWindow
+ popoutLink="#" // TODO: Change once Document Classification is added.
+ popoutLinkLabel="User"
/>
diff --git a/client/src/components/location/location-comments-form.tsx b/client/src/components/location/location-comments-form.tsx
index 0180fe1bb..8c6898b4e 100644
--- a/client/src/components/location/location-comments-form.tsx
+++ b/client/src/components/location/location-comments-form.tsx
@@ -67,6 +67,9 @@ export function LocationCommentForm({
isFetchError={isCommentTypeError}
placeholder="Comment Type"
description="Specify the category of the comment from the available options."
+ popoutLink="/dispatch/comment-types/"
+ hasPopoutWindow
+ popoutLinkLabel="Comment Type"
/>
diff --git a/client/src/components/location/location-info-form.tsx b/client/src/components/location/location-info-form.tsx
index 637292c96..393940251 100644
--- a/client/src/components/location/location-info-form.tsx
+++ b/client/src/components/location/location-info-form.tsx
@@ -109,11 +109,12 @@ export function LocationInfoForm({
options={selectLocationCategories}
isFetchError={isError}
isLoading={isLoading}
- hasPopoutWindow
- popoutLink="/dispatch/location-categories/"
isClearable
placeholder="Select Location Category"
description="Choose the category that best describes the location's function or type."
+ hasPopoutWindow
+ popoutLink="/dispatch/location-categories/"
+ popoutLinkLabel="Location Category"
/>
@@ -129,6 +130,9 @@ export function LocationInfoForm({
isLoading={isDepotsLoading}
placeholder="Select Depot"
description="Select the depot or main hub that this location is associated with."
+ hasPopoutWindow
+ popoutLink="#"
+ popoutLinkLabel="Depot"
/>
diff --git a/client/src/components/revenue-codes/rc-table-dialog.tsx b/client/src/components/revenue-codes/rc-table-dialog.tsx
index 82345ffa2..24f7134fc 100644
--- a/client/src/components/revenue-codes/rc-table-dialog.tsx
+++ b/client/src/components/revenue-codes/rc-table-dialog.tsx
@@ -88,6 +88,9 @@ export function RCForm({
placeholder="Select Expense Account"
description="The Expense Account associated with the Revenue Code"
isClearable
+ hasPopoutWindow
+ popoutLink="/accounting/gl-accounts"
+ popoutLinkLabel="GL Account"
/>
@@ -102,6 +105,9 @@ export function RCForm({
placeholder="Select Revenue Account"
description="The Revneue Account associated with the Revenue Code"
isClearable
+ hasPopoutWindow
+ popoutLink="/accounting/gl-accounts"
+ popoutLinkLabel="GL Account"
/>
diff --git a/client/src/components/tractors/tractor-table-dialog.tsx b/client/src/components/tractors/tractor-table-dialog.tsx
index 945aa82c5..ba60220a3 100644
--- a/client/src/components/tractors/tractor-table-dialog.tsx
+++ b/client/src/components/tractors/tractor-table-dialog.tsx
@@ -126,6 +126,7 @@ export function TractorForm({
description="Select the equipment type of the tractor, to categorize it based on its functionality and usage."
popoutLink="/equipment/equipment-types/"
hasPopoutWindow
+ popoutLinkLabel="Equipment Type"
/>
@@ -139,8 +140,9 @@ export function TractorForm({
placeholder="Select Manufacturer"
description="Select the manufacturer of the tractor, to categorize it based on its functionality and usage."
isClearable
- popoutLink="/equipment/equipment-manufacturers/"
hasPopoutWindow
+ popoutLink="/equipment/equipment-manufacturers/"
+ popoutLinkLabel="Equipment Manufacturer"
/>
@@ -204,6 +206,7 @@ export function TractorForm({
hasPopoutWindow
popoutLink="/dispatch/fleet-codes/"
isClearable
+ popoutLinkLabel="Fleet Code"
/>
@@ -217,6 +220,9 @@ export function TractorForm({
placeholder="Select Primary Worker"
description="Select the primary worker assigned to the tractor."
isClearable
+ hasPopoutWindow
+ popoutLink="#"
+ popoutLinkLabel="Worker"
/>
@@ -230,6 +236,9 @@ export function TractorForm({
placeholder="Select Secondary Worker"
description="Select the secondary worker assigned to the tractor."
isClearable
+ hasPopoutWindow
+ popoutLink="#"
+ popoutLinkLabel="Worker"
/>
diff --git a/client/src/components/trailers/trailer-table-dialog.tsx b/client/src/components/trailers/trailer-table-dialog.tsx
index 2f7947d4e..7f5efa8e4 100644
--- a/client/src/components/trailers/trailer-table-dialog.tsx
+++ b/client/src/components/trailers/trailer-table-dialog.tsx
@@ -120,6 +120,7 @@ export function TrailerForm({
isClearable={false}
popoutLink="/equipment/equipment-types/"
hasPopoutWindow
+ popoutLinkLabel="Equipment Types"
/>
@@ -133,8 +134,9 @@ export function TrailerForm({
placeholder="Select Manufacturer"
description="Select the manufacturer of the trailer, to categorize it based on its functionality and usage."
isClearable={false}
- popoutLink="/equipment/equipment-manufacturers/"
hasPopoutWindow
+ popoutLink="/equipment/equipment-manufacturers/"
+ popoutLinkLabel="Equipment Manufacturer"
/>
@@ -195,6 +197,7 @@ export function TrailerForm({
description="Select the code that identifies the trailer within your fleet."
hasPopoutWindow
popoutLink="/dispatch/fleet-codes/"
+ popoutLinkLabel="Fleet Code"
isClearable
/>
From 90dd95812af58ff990f02db21cebf279c42630f0 Mon Sep 17 00:00:00 2001
From: Eric Moss
Date: Tue, 19 Dec 2023 19:51:12 -0500
Subject: [PATCH 06/14] add: popout link label to select comp.
---
client/src/components/common/fields/select-components.tsx | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/client/src/components/common/fields/select-components.tsx b/client/src/components/common/fields/select-components.tsx
index 3aac9255a..0a7a1fbb9 100644
--- a/client/src/components/common/fields/select-components.tsx
+++ b/client/src/components/common/fields/select-components.tsx
@@ -176,7 +176,7 @@ export function MenuList({
return (
{child}
@@ -200,7 +200,6 @@ export function NoOptionsMessage({
formError?: string;
popoutLink?: string;
hasPopoutWindow?: boolean;
- label: string;
};
}) {
const { popoutLink, hasPopoutWindow } = props.selectProps || {};
@@ -215,7 +214,7 @@ export function NoOptionsMessage({
{popoutLink && hasPopoutWindow && (
)}
From c78f31162b6737fec4531418bc4f9154e17ce4e7 Mon Sep 17 00:00:00 2001
From: Eric Moss
Date: Tue, 19 Dec 2023 19:51:24 -0500
Subject: [PATCH 07/14] fix: charge type edit form props
---
.../charge-types/charge-type-edit-dialog.tsx | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/client/src/components/charge-types/charge-type-edit-dialog.tsx b/client/src/components/charge-types/charge-type-edit-dialog.tsx
index c90dfde3b..9fc651c8c 100644
--- a/client/src/components/charge-types/charge-type-edit-dialog.tsx
+++ b/client/src/components/charge-types/charge-type-edit-dialog.tsx
@@ -19,7 +19,10 @@ import { useCustomMutation } from "@/hooks/useCustomMutation";
import { formatDate } from "@/lib/date";
import { chargeTypeSchema } from "@/lib/validations/BillingSchema";
import { useTableStore } from "@/stores/TableStore";
-import { ChargeTypeFormValues as FormValues } from "@/types/billing";
+import {
+ ChargeType,
+ ChargeTypeFormValues as FormValues,
+} from "@/types/billing";
import { TableSheetProps } from "@/types/tables";
import { yupResolver } from "@hookform/resolvers/yup";
import React from "react";
@@ -35,11 +38,7 @@ import {
} from "../ui/dialog";
import { ChargeTypeForm } from "./charge-type-dialog";
-function ChargeTypeEditForm({
- chargeType,
-}: {
- chargeType: Record;
-}) {
+function ChargeTypeEditForm({ chargeType }: { chargeType: ChargeType }) {
const [isSubmitting, setIsSubmitting] = React.useState(false);
const { control, reset, handleSubmit } = useForm({
resolver: yupResolver(chargeTypeSchema),
From 9539960d8b2ee25448e59dc8705a310c686d629b Mon Sep 17 00:00:00 2001
From: Eric Moss
Date: Tue, 19 Dec 2023 21:02:31 -0500
Subject: [PATCH 08/14] change: fleet code to max len 10
---
server/dispatch/models.py | 27 +--------------------------
1 file changed, 1 insertion(+), 26 deletions(-)
diff --git a/server/dispatch/models.py b/server/dispatch/models.py
index 413ebceae..78756695e 100644
--- a/server/dispatch/models.py
+++ b/server/dispatch/models.py
@@ -282,31 +282,6 @@ class FleetCode(GenericModel):
A FleetCode instance represents a code used to identify a fleet of vehicles for service incidents.
This allows for tracking and reporting on specific fleets of vehicles, including their revenue goals,
deadhead goals, and mileage goals.
-
- Attributes:
- code (CharField): Fleet code for the service incident.
- Has a max length of 4 characters, is the primary key and unique, with help text of "Fleet code for the
- service incident.".
- description (CharField): Description for the fleet code.
- Has a max length of 100 characters and help text of "Description for the fleet code.".
- status (ChoiceField): Whether the fleet code is active.
- Has a default value of True and help text of "Is the fleet code active.".
- revenue_goal (DecimalField): Revenue goal for the fleet code.
- Has a maximum of 10 digits, 2 decimal places, a default value of 0.00, and help text of "Revenue goal for
- the fleet code.".
- deadhead_goal (DecimalField): Deadhead goal for the fleet code.
- Has a maximum of 10 digits, 2 decimal places, a default value of 0.00, and help text of "Deadhead goal for
- the fleet code.".
- mileage_goal (DecimalField): Mileage goal for the fleet code.
- Has a maximum of 10 digits, 2 decimal places, a default value of 0.00, and help text of "Mileage goal for
- the fleet code.".
-
- Methods:
- __str__(self) -> str:
- Returns a string representation of the fleet code, wrapped to a maximum of 50 characters.
-
- get_absolute_url(self) -> str:
- Returns the URL for this object's detail view.
"""
id = models.UUIDField(
@@ -323,7 +298,7 @@ class FleetCode(GenericModel):
)
code = models.CharField(
_("Fleet Code"),
- max_length=4,
+ max_length=10,
help_text=_("Fleet code for the service incident."),
)
description = models.CharField(
From d9d70db81d073671ce076ab717f3a81a8c0a97a6 Mon Sep 17 00:00:00 2001
From: Eric Moss
Date: Tue, 19 Dec 2023 21:02:35 -0500
Subject: [PATCH 09/14] Update react-select-extension.d.ts
---
client/src/types/react-select-extension.d.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client/src/types/react-select-extension.d.ts b/client/src/types/react-select-extension.d.ts
index 6d49796a9..b85945934 100644
--- a/client/src/types/react-select-extension.d.ts
+++ b/client/src/types/react-select-extension.d.ts
@@ -15,7 +15,7 @@
* Grant, and not modifying the license in any other way.
*/
-import { Options } from "react-select"; // eslint-disable-line import/no-unassigned-import
+import { GroupBase, Options } from "react-select"; // eslint-disable-line import/no-unassigned-import
type _ = Options; // Added just so auto sort import won't remove the import
From 0e4cd755f16e43282452b2b268d55d83a1eb13fb Mon Sep 17 00:00:00 2001
From: Eric Moss
Date: Tue, 19 Dec 2023 21:02:57 -0500
Subject: [PATCH 10/14] fix: fleet code fields being required when not
---
client/src/lib/validations/DispatchSchema.ts | 47 ++++++++++++++------
client/src/types/dispatch.ts | 8 ++--
2 files changed, 37 insertions(+), 18 deletions(-)
diff --git a/client/src/lib/validations/DispatchSchema.ts b/client/src/lib/validations/DispatchSchema.ts
index e0d87319c..f2c07fdba 100644
--- a/client/src/lib/validations/DispatchSchema.ts
+++ b/client/src/lib/validations/DispatchSchema.ts
@@ -1,3 +1,19 @@
+/*
+ * COPYRIGHT(c) 2023 MONTA
+ *
+ * This file is part of Monta.
+ *
+ * The Monta software is licensed under the Business Source License 1.1. You are granted the right
+ * to copy, modify, and redistribute the software, but only for non-production use or with a total
+ * of less than three server instances. Starting from the Change Date (November 16, 2026), the
+ * software will be made available under version 2 or later of the GNU General Public License.
+ * If you use the software in violation of this license, your rights under the license will be
+ * terminated automatically. The software is provided "as is," and the Licensor disclaims all
+ * warranties and conditions. If you use this license's text or the "Business Source License" name
+ * and trademark, you must comply with the Licensor's covenants, which include specifying the
+ * Change License as the GPL Version 2.0 or a compatible license, specifying an Additional Use
+ * Grant, and not modifying the license in any other way.
+ */
import { validateDecimal } from "@/lib/utils";
/*
* COPYRIGHT(c) 2023 MONTA
@@ -15,7 +31,6 @@ import { validateDecimal } from "@/lib/utils";
* Change License as the GPL Version 2.0 or a compatible license, specifying an Additional Use
* Grant, and not modifying the license in any other way.
*/
-
import * as Yup from "yup";
import { ObjectSchema } from "yup";
import {
@@ -68,41 +83,45 @@ export const fleetCodeSchema: ObjectSchema =
status: Yup.string().required("Status is required"),
code: Yup.string()
.required("Name is required")
- .max(4, "Code cannot be more than 4 characters"),
+ .max(10, "Code cannot be more than 10 characters"),
revenueGoal: Yup.string()
- .required("Revenue Goal is required")
+ .notRequired()
+ .nullable()
.test(
"is-decimal",
"Revenue Goal must be a decimal with no more than two decimal places",
(value) => {
- if (value !== undefined && value !== null) {
- return validateDecimal(value, 2);
+ if (value === undefined || value === null || value === "") {
+ return true; // Passes validation for null, undefined, or empty string
}
- return false;
+ return validateDecimal(value, 2);
},
),
deadheadGoal: Yup.string()
- .required("Deadhead Goal is required")
+ .notRequired()
+ .nullable()
.test(
"is-decimal",
"Deadhead Goal must be a decimal with no more than two decimal places",
(value) => {
- if (value !== undefined && value !== null) {
- return validateDecimal(value, 2);
+ console.info("value", value);
+ if (value === undefined || value === null || value === "") {
+ return true; // Passes validation for null, undefined, or empty string
}
- return false;
+ return validateDecimal(value, 2);
},
),
mileageGoal: Yup.string()
- .required("Mileage Goal is required")
+ .notRequired()
+ .nullable()
.test(
"is-decimal",
"Mileage Goal must be a decimal with no more than two decimal places",
(value) => {
- if (value !== undefined && value !== null) {
- return validateDecimal(value, 2);
+ if (value === undefined || value === null || value === "") {
+ return true; // Passes validation for null, undefined, or empty string
}
- return false;
+ return validateDecimal(value, 2);
},
),
description: Yup.string()
diff --git a/client/src/types/dispatch.ts b/client/src/types/dispatch.ts
index fc1e5ecc6..c80f5b770 100644
--- a/client/src/types/dispatch.ts
+++ b/client/src/types/dispatch.ts
@@ -49,7 +49,7 @@ export interface DelayCode extends BaseModel {
export type DelayCodeFormValues = Omit<
DelayCode,
- "organization" | "businessUnit" | "created" | "modified"
+ "id" | "organization" | "businessUnit" | "created" | "modified"
>;
export interface FleetCode extends BaseModel {
@@ -65,7 +65,7 @@ export interface FleetCode extends BaseModel {
export type FleetCodeFormValues = Omit<
FleetCode,
- "organization" | "businessUnit" | "created" | "modified"
+ "id" | "organization" | "businessUnit" | "created" | "modified"
>;
export interface CommentType extends BaseModel {
@@ -87,8 +87,8 @@ export interface Rate extends BaseModel {
isActive: boolean;
rateNumber: string;
customer?: string | null;
- effectiveDate: Date;
- expirationDate: Date;
+ effectiveDate: string;
+ expirationDate: string;
commodity?: string | null;
orderType?: string | null;
equipmentType?: string | null;
From 74d2b06a1b6818dc15a0aba558879ac5cc947ba6 Mon Sep 17 00:00:00 2001
From: Eric Moss
Date: Tue, 19 Dec 2023 21:03:13 -0500
Subject: [PATCH 11/14] chore: update comments and remove unused function
---
client/src/lib/utils.ts | 24 +++++++++---------------
1 file changed, 9 insertions(+), 15 deletions(-)
diff --git a/client/src/lib/utils.ts b/client/src/lib/utils.ts
index acfac07c5..23daad0e8 100644
--- a/client/src/lib/utils.ts
+++ b/client/src/lib/utils.ts
@@ -14,7 +14,7 @@
* Change License as the GPL Version 2.0 or a compatible license, specifying an Additional Use
* Grant, and not modifying the license in any other way.
*/
-import { clsx, type ClassValue } from "clsx";
+import { type ClassValue, clsx } from "clsx";
import { RefObject, useEffect } from "react";
import { twMerge } from "tailwind-merge";
@@ -24,8 +24,8 @@ export function cn(...inputs: ClassValue[]) {
/**
* Formats a date string into a human readable format
- * @param dateStr - The date string to format
* @returns {string}
+ * @param str
*/
export function upperFirst(str: string): string {
if (!str) return "";
@@ -68,9 +68,9 @@ export function truncateText(str: string, length: number): string {
/**
* Returns a random integer between min (inclusive) and max (inclusive).
- * @param min - The minimum value to return
- * @param max - The maximum value to return
* @returns {number}
+ * @param ref
+ * @param handler
*/
export const useClickOutside = (
ref: RefObject,
@@ -174,6 +174,11 @@ type PopoutWindowParams = {
* Opens a new window with the given path and query params
* @param path - The path to open the new window to
* @param incomingQueryParams - The query params to pass to the new window
+ * @param width - The width of the new window
+ * @param height - The height of the new window
+ * @param left - The left position of the new window
+ * @param top - The top position of the new window
+ * @param hideHeader - Whether or not to hide the header of the new window
* @returns {void}
*/
export function PopoutWindow(
@@ -219,14 +224,3 @@ export const cleanObject = (obj: Record): Record => {
});
return cleanedObj;
};
-
-/**
- * Converts a camelCase string to a readable string
- * @param str
- * @returns {string}
- */
-export const convertCamelCaseToReadable = (str: string): string => {
- return str
- .replace(/([A-Z])/g, " $1")
- .replace(/^./, (str) => str.toUpperCase());
-};
From 28d714ece3a5d340faa405f6baa95bfa2d7481c0 Mon Sep 17 00:00:00 2001
From: Eric Moss
Date: Tue, 19 Dec 2023 21:03:28 -0500
Subject: [PATCH 12/14] change: fleet code to new form components
---
.../fleet-codes/fleet-code-table-dialog.tsx | 48 ++++++++++---------
1 file changed, 25 insertions(+), 23 deletions(-)
diff --git a/client/src/components/fleet-codes/fleet-code-table-dialog.tsx b/client/src/components/fleet-codes/fleet-code-table-dialog.tsx
index 45fb1bd36..7837c34be 100644
--- a/client/src/components/fleet-codes/fleet-code-table-dialog.tsx
+++ b/client/src/components/fleet-codes/fleet-code-table-dialog.tsx
@@ -37,6 +37,8 @@ import { TableSheetProps } from "@/types/tables";
import { yupResolver } from "@hookform/resolvers/yup";
import React from "react";
import { Control, useForm } from "react-hook-form";
+import { Form, FormControl, FormGroup } from "@/components/ui/form";
+import { cleanObject } from "@/lib/utils";
export function FleetCodeForm({
control,
@@ -48,9 +50,9 @@ export function FleetCodeForm({
const { selectUsersData, isLoading, isError } = useUsers(open);
return (
-
-
-
+
-
+
+
-
-
-
+
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
+
+
+
);
}
@@ -171,8 +171,10 @@ export function FleetCodeDialog({ onOpenChange, open }: TableSheetProps) {
);
const onSubmit = (values: FormValues) => {
+ const cleanedValues = cleanObject(values);
+
setIsSubmitting(true);
- mutation.mutate(values);
+ mutation.mutate(cleanedValues);
};
return (
From 939cd94bcb3fa006fd933b56ec126897b3871695 Mon Sep 17 00:00:00 2001
From: Eric Moss
Date: Tue, 19 Dec 2023 21:03:39 -0500
Subject: [PATCH 13/14] change: react-select
---
client/src/components/common/fields/async-select-input.tsx | 5 ++++-
client/src/components/common/fields/select-input.tsx | 1 +
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/client/src/components/common/fields/async-select-input.tsx b/client/src/components/common/fields/async-select-input.tsx
index 1a494e8ef..1dbe6b30a 100644
--- a/client/src/components/common/fields/async-select-input.tsx
+++ b/client/src/components/common/fields/async-select-input.tsx
@@ -29,7 +29,7 @@ import {
ValueProcessor,
} from "@/components/common/fields/select-components";
import { cn } from "@/lib/utils";
-import { UseControllerProps, useController } from "react-hook-form";
+import { useController, UseControllerProps } from "react-hook-form";
import { GroupBase, OptionsOrGroups, Props } from "react-select";
import AsyncSelect from "react-select/async";
@@ -49,6 +49,9 @@ interface AsyncSelectInputProps>
options: OptionsOrGroups>;
hasContextMenu?: boolean;
isFetchError?: boolean;
+ hasPopoutWindow?: boolean;
+ popoutLink?: string;
+ popoutLinkLabel?: string;
}
/**
diff --git a/client/src/components/common/fields/select-input.tsx b/client/src/components/common/fields/select-input.tsx
index 12df3a31d..5e2abb09a 100644
--- a/client/src/components/common/fields/select-input.tsx
+++ b/client/src/components/common/fields/select-input.tsx
@@ -53,6 +53,7 @@ interface SelectInputProps>
isFetchError?: boolean;
hasPopoutWindow?: boolean; // Set to true to open the popout window
popoutLink?: string; // Link to the popout page
+ popoutLinkLabel?: string; // Label for the popout link
}
/**
From 6b1bed8682afaf367289cbc0421d6345917e25c0 Mon Sep 17 00:00:00 2001
From: Eric Moss
Date: Tue, 19 Dec 2023 21:03:42 -0500
Subject: [PATCH 14/14] Update customer-table-sub.tsx
---
.../customer/customer-table-sub.tsx | 48 ++++++++++++++-----
1 file changed, 35 insertions(+), 13 deletions(-)
diff --git a/client/src/components/customer/customer-table-sub.tsx b/client/src/components/customer/customer-table-sub.tsx
index aee38d28d..58989065f 100644
--- a/client/src/components/customer/customer-table-sub.tsx
+++ b/client/src/components/customer/customer-table-sub.tsx
@@ -15,7 +15,10 @@
* Grant, and not modifying the license in any other way.
*/
-import { Button } from "@/components/ui/button";
+import {
+ BoolStatusBadge,
+ DataNotFound,
+} from "@/components/common/table/data-table-components";
import {
Table,
TableBody,
@@ -24,17 +27,19 @@ import {
TableHeader,
TableRow,
} from "@/components/ui/table";
-import { truncateText } from "@/lib/utils";
+import { cn, truncateText } from "@/lib/utils";
import { useCustomerFormStore } from "@/stores/CustomerStore";
import { useTableStore } from "@/stores/TableStore";
import { Customer } from "@/types/customer";
+import { CircleBackslashIcon, PersonIcon } from "@radix-ui/react-icons";
import { Row } from "@tanstack/react-table";
import React from "react";
import {
- BoolStatusBadge,
- DataNotFound,
-} from "@/components/common/table/data-table-components";
-import { CircleBackslashIcon, PersonIcon } from "@radix-ui/react-icons";
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "../ui/tooltip";
const daysOfWeek = [
"Monday", // 0
@@ -62,6 +67,9 @@ function CustomerContactTable({