Skip to content

Commit

Permalink
feat: table change alert #203
Browse files Browse the repository at this point in the history
  • Loading branch information
emoss08 committed Jan 26, 2024
1 parent a6279d7 commit 34573b6
Show file tree
Hide file tree
Showing 3 changed files with 354 additions and 2 deletions.
67 changes: 67 additions & 0 deletions client/public/locales/en/admin.tablechangealert.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"title": "Create New Table Change Alert",
"subTitle": "Use this form to add a new table change alert to the system.",
"formMessages": {
"postSuccess": "Table Change Alert saved successfully.",
"postError": "There was an error saving the table change alert.",
"updateSuccess": "Email profile updated successfully.",
"updateError": "There was an error updating the table change alert."
},
"fields": {
"status": {
"placeholder": "Status",
"label": "Status",
"description": "Indicates the current state of the table change alert. Reflects the operational status within the system."
},
"name": {
"placeholder": "Name",
"label": "Name",
"description": "A unique identifier for the table change alert. This name helps in easily referencing and managing the alert."
},
"databaseAction": {
"placeholder": "Database Action",
"label": "Database Action",
"description": "Specifies the type of database operation that triggers the alert."
},
"source": {
"placeholder": "Source",
"label": "Source",
"description": "Identifies the origin or the data source for the table change alert, such as a specific database or data stream."
},
"topic": {
"placeholder": "Topic",
"label": "Topic",
"description": "Defines the main subject or the table name associated with the alert. This field usually aligns with the specific table being monitored."
},
"table": {
"placeholder": "Table",
"label": "Table",
"description": "The exact name of the database table to which the alert is attached. Changes in this table trigger the alert."
},
"description": {
"placeholder": "Description",
"label": "Description",
"description": "A detailed explanation of the table change alert's purpose, scope, and any relevant details. This helps users understand the alert's context."
},
"emailProfile": {
"placeholder": "Email Profile",
"label": "Email Profile",
"description": "Refers to the configured email settings profile used for sending out the alerts. This includes sender information and SMTP settings."
},
"emailRecipients": {
"placeholder": "Email Recipients",
"label": "Email Recipients",
"description": "A list of email addresses, separated by commas, that will receive notifications. These recipients are alerted upon the specified database action."
},
"effectiveDate": {
"placeholder": "Effective Date",
"label": "Effective Date",
"description": "The start date from which the table change alert becomes active."
},
"expirationDate": {
"placeholder": "Expiration Date",
"label": "Expiration Date",
"description": "The date after which the table change alert becomes inactive."
}
}
}
285 changes: 285 additions & 0 deletions client/src/components/table-change-alerts/table-change-sheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
/*
* COPYRIGHT(c) 2024 Trenova
*
* This file is part of Trenova.
*
* The Trenova 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 { DatepickerField } from "@/components/common/fields/date-picker";
import { TextareaField } from "@/components/common/fields/textarea";
import { Button } from "@/components/ui/button";
import {
Sheet,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
} from "@/components/ui/sheet";
import { useCustomMutation } from "@/hooks/useCustomMutation";
import { useEmailProfiles, useTableNames } from "@/hooks/useQueries";
import {
databaseActionChoices,
sourceChoices,
statusChoices,
} from "@/lib/choices";
import { cleanObject, cn } from "@/lib/utils";
import { tableChangeAlertSchema } from "@/lib/validations/OrganizationSchema";
import { TableChangeAlertFormValues as FormValues } from "@/types/organization";
import { TableSheetProps } from "@/types/tables";
import { yupResolver } from "@hookform/resolvers/yup";
import { useState } from "react";
import { Control, UseFormWatch, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { InputField } from "../common/fields/input";
import { SelectInput } from "../common/fields/select-input";
import { Form, FormControl, FormGroup } from "../ui/form";

function SourceField({
sourceChoice,
control,
}: {
sourceChoice: string;
control: Control<FormValues>;
}) {
const { t } = useTranslation("admin.tablechangealert");
const { selectTableNames, isError, isLoading } = useTableNames();

return sourceChoice === "KAFKA" ? (
<FormControl>
<SelectInput
name="topic"
rules={{ required: true }}
options={sourceChoices}
control={control}
label={t("fields.topic.label")}
placeholder={t("fields.topic.placeholder")}
description={t("fields.topic.description")}
/>
</FormControl>
) : (
<FormControl>
<SelectInput
name="table"
rules={{ required: true }}
options={selectTableNames}
isLoading={isLoading}
isFetchError={isError}
control={control}
label={t("fields.table.label")}
placeholder={t("fields.table.placeholder")}
description={t("fields.table.description")}
/>
</FormControl>
);
}

export function TableChangeAlertForm({
control,
open,
watch,
}: {
control: Control<FormValues>;
open: boolean;
watch: UseFormWatch<FormValues>;
}) {
const { t } = useTranslation("admin.tablechangealert");

const sourceChoice = watch("source");

const { selectEmailProfile, isError, isLoading } = useEmailProfiles(open);

return (
<Form>
<FormGroup className="lg:grid-cols-2">
<FormControl>
<SelectInput
name="status"
rules={{ required: true }}
control={control}
options={statusChoices}
isClearable={false}
label={t("fields.status.label")}
placeholder={t("fields.status.placeholder")}
description={t("fields.status.description")}
/>
</FormControl>
<FormControl>
<InputField
name="name"
rules={{ required: true }}
control={control}
label={t("fields.name.label")}
placeholder={t("fields.name.placeholder")}
description={t("fields.name.description")}
/>
</FormControl>
<FormControl>
<SelectInput
name="databaseAction"
rules={{ required: true }}
options={databaseActionChoices}
control={control}
label={t("fields.databaseAction.label")}
placeholder={t("fields.databaseAction.placeholder")}
description={t("fields.databaseAction.description")}
/>
</FormControl>
<FormControl>
<SelectInput
name="source"
rules={{ required: true }}
options={sourceChoices}
control={control}
label={t("fields.source.label")}
placeholder={t("fields.source.placeholder")}
description={t("fields.source.description")}
/>
</FormControl>
</FormGroup>
<FormGroup className="md:grid-cols-1 lg:grid-cols-1">
<SourceField sourceChoice={sourceChoice} control={control} />
<FormControl>
<TextareaField
name="description"
control={control}
label={t("fields.description.label")}
placeholder={t("fields.description.placeholder")}
description={t("fields.description.description")}
/>
</FormControl>
</FormGroup>
<FormGroup className="lg:grid-cols-2 xl:grid-cols-2">
<FormControl>
<SelectInput
name="emailProfile"
options={selectEmailProfile}
isFetchError={isError}
isLoading={isLoading}
control={control}
label={t("fields.emailProfile.label")}
placeholder={t("fields.emailProfile.placeholder")}
description={t("fields.emailProfile.description")}
/>
</FormControl>
<FormControl>
<InputField
name="emailRecipients"
rules={{ required: true }}
control={control}
label={t("fields.emailRecipients.label")}
placeholder={t("fields.emailRecipients.placeholder")}
description={t("fields.emailRecipients.description")}
/>
</FormControl>
<FormControl>
<DatepickerField
name="effectiveDate"
control={control}
label={t("fields.effectiveDate.label")}
placeholder={t("fields.effectiveDate.placeholder")}
description={t("fields.effectiveDate.description")}
/>
</FormControl>
<FormControl>
<DatepickerField
name="expirationDate"
control={control}
label={t("fields.expirationDate.label")}
placeholder={t("fields.expirationDate.placeholder")}
description={t("fields.expirationDate.description")}
/>
</FormControl>
</FormGroup>
</Form>
);
}

export function TableChangeAlertSheet({ onOpenChange, open }: TableSheetProps) {
const { t } = useTranslation(["admin.tablechangealert", "common"]);
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);

const { control, reset, handleSubmit, watch } = useForm<FormValues>({
resolver: yupResolver(tableChangeAlertSchema),
defaultValues: {
status: "A",
name: "",
databaseAction: "INSERT",
table: "",
source: "POSTGRES",
topic: "",
description: "",
emailProfile: "",
emailRecipients: "",
conditionalLogic: {},
customSubject: "",
effectiveDate: "",
expirationDate: "",
},
});

const mutation = useCustomMutation<FormValues>(
control,
{
method: "POST",
path: "/table_change_alerts/",
successMessage: t("formMessages.postSuccess"),
queryKeysToInvalidate: ["table-change-alert-data"],
closeModal: true,
errorMessage: t("formMessages.postError"),
},
() => setIsSubmitting(false),
reset,
);
const onSubmit = (values: FormValues) => {
const cleanedValues = cleanObject(values);
setIsSubmitting(true);
mutation.mutate(cleanedValues);
};

return (
<Sheet open={open} onOpenChange={onOpenChange}>
<SheetContent className={cn("w-full xl:w-[700px]")}>
<SheetHeader>
<SheetTitle>{t("title")}</SheetTitle>
<SheetDescription>{t("subTitle")}</SheetDescription>
</SheetHeader>
<form
onSubmit={handleSubmit(onSubmit)}
className="flex h-full flex-col overflow-y-auto"
>
{/* <TableChangeAlertNotice /> */}
<TableChangeAlertForm control={control} open={open} watch={watch} />
<SheetFooter className="mb-12">
<Button
type="reset"
variant="secondary"
onClick={() => onOpenChange(false)}
className="w-full"
>
{t("buttons.cancel", { ns: "common" })}
</Button>
<Button
type="submit"
isLoading={isSubmitting}
loadingText={t("buttons.savingText", { ns: "common" })}
className="w-full"
>
{t("buttons.save", { ns: "common" })}
</Button>
</SheetFooter>
</form>
</SheetContent>
</Sheet>
);
}
4 changes: 2 additions & 2 deletions client/src/pages/admin/TableChangeAlerts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import AdminLayout from "@/components/admin-page/layout";
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 { EmailProfileDialog } from "@/components/email-profile/email-profile-table-dialog";
import { EmailProfileTableEditDialog } from "@/components/email-profile/email-profile-table-edit-dialog";
import { TableChangeAlertSheet } from "@/components/table-change-alerts/table-change-sheet";
import { TableChangeAlert } from "@/types/organization";
import { ColumnDef } from "@tanstack/react-table";

Expand Down Expand Up @@ -79,7 +79,7 @@ export default function TableChangeAlerts() {
name="Table Change Alert"
exportModelName="TableChangeAlert"
filterColumn="name"
TableSheet={EmailProfileDialog}
TableSheet={TableChangeAlertSheet}
TableEditSheet={EmailProfileTableEditDialog}
addPermissionName="view_tablechangealert"
/>
Expand Down

0 comments on commit 34573b6

Please sign in to comment.