-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
354 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
285
client/src/components/table-change-alerts/table-change-sheet.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters