feat: regex hint

This commit is contained in:
Abhinav Raut
2025-04-13 19:28:58 +05:30
parent 91372f5339
commit 0254bab266
11 changed files with 54 additions and 21 deletions

View File

@@ -87,6 +87,16 @@ export const adminNavItems = [
}
]
},
{
titleKey: 'navigation.customAttributes',
children: [
{
titleKey: 'navigation.customAttributes',
href: '/admin/custom-attributes',
permission: 'custom_attributes:manage'
}
]
},
{
titleKey: 'navigation.notifications',
children: [
@@ -117,16 +127,6 @@ export const adminNavItems = [
}
]
},
{
titleKey: 'globals.terms.customAttribute',
children: [
{
titleKey: 'globals.terms.customAttribute',
href: '/admin/custom-attributes',
permission: 'custom_attributes:manage'
}
]
}
]
export const accountNavItems = [

View File

@@ -119,6 +119,19 @@
</FormItem>
</FormField>
<FormField name="regex_hint" v-slot="{ componentField }">
<FormItem v-show="form.values.data_type === 'text'">
<FormLabel> {{ $t('form.field.regexHint') }} ({{ $t('form.field.optional') }}) </FormLabel>
<FormControl>
<Input type="text" v-bind="componentField" />
</FormControl>
<FormDescription>
{{ $t('admin.customAttributes.regexHint.description') }}
</FormDescription>
<FormMessage />
</FormItem>
</FormField>
<!-- Form submit button slot -->
<slot name="footer"></slot>
</form>

View File

@@ -61,6 +61,7 @@ export const createFormSchema = (t) => z.object({
required_error: t('globals.messages.required'),
}),
regex: z.string().optional(),
regex_hint: z.string().optional(),
values: z.array(z.string())
.default([])
})

View File

@@ -143,6 +143,7 @@
</AccordionTrigger>
<AccordionContent class="p-4">
<CustomAttributes
:loading="conversationStore.current.loading"
:attributes="customAttributeStore.contactAttributeOptions"
:customAttributes="conversationStore.current?.contact?.custom_attributes || {}"
@update:setattributes="updateContactCustomAttributes"

View File

@@ -9,7 +9,7 @@
</p>
<Tooltip>
<TooltipTrigger>
<Info class="text-muted-foreground" size="16" />
<Info class="text-muted-foreground" size="14" />
</TooltipTrigger>
<TooltipContent>
{{ attribute.description }}
@@ -36,7 +36,7 @@
</p>
<Tooltip>
<TooltipTrigger>
<Info class="text-muted-foreground" size="16" />
<Info class="text-muted-foreground" size="14" />
</TooltipTrigger>
<TooltipContent>
{{ attribute.description }}
@@ -177,13 +177,13 @@ const getValidationSchema = (attribute) => {
switch (attribute.data_type) {
case 'text': {
let schema = z.string().min(1, t('globals.messages.required'))
// If regex is provided and valid, add it to the schema validation.
// If regex is provided and valid, add it to the schema validation along with the hint
if (attribute.regex) {
try {
console.log('Creating regex:', attribute.regex)
const regex = new RegExp(attribute.regex)
schema = schema.regex(regex, {
message: t('globals.messages.invalid', { name: t('form.field.value').toLowerCase() })
message: attribute.regex_hint
})
} catch (err) {
console.error('Error creating regex:', err)

View File

@@ -246,8 +246,10 @@
"navigation.views": "Views",
"navigation.reassignReplies": "Reassign replies",
"navigation.allContacts": "All Contacts",
"navigation.customAttributes": "Custom Attributes",
"form.field.name": "Name",
"form.field.regex": "Regex",
"form.field.regexHint": "Regex hint",
"form.field.key": "Key",
"form.field.type": "Type",
"form.field.listValues": "List values",
@@ -607,5 +609,6 @@
"contact.unblockConfirm": "Are you sure you want to unblock this contact? They will be able to interact with you again.",
"contact.alreadyExistsWithEmail": "Another contact with same email already exists",
"admin.customAttributes.deleteConfirmation": "This action cannot be undone. This will permanently delete this custom attribute.",
"admin.customAttributes.regex.description": "Regex to validate the value of this custom attribute. Leave empty to skip validation."
"admin.customAttributes.regex.description": "Regex to validate the value of this custom attribute. Leave empty to skip validation.",
"admin.customAttributes.regexHint.description": "Regex pattern hint."
}

View File

@@ -80,7 +80,7 @@ func (m *Manager) GetAll(appliesTo string) ([]models.CustomAttribute, error) {
// Create creates a new custom attribute.
func (m *Manager) Create(attr models.CustomAttribute) error {
if _, err := m.q.InsertCustomAttribute.Exec(attr.AppliesTo, attr.Name, attr.Description, attr.Key, pq.Array(attr.Values), attr.DataType, attr.Regex); err != nil {
if _, err := m.q.InsertCustomAttribute.Exec(attr.AppliesTo, attr.Name, attr.Description, attr.Key, pq.Array(attr.Values), attr.DataType, attr.Regex, attr.RegexHint); err != nil {
if dbutil.IsUniqueViolationError(err) {
return envelope.NewError(envelope.InputError, m.i18n.Ts("globals.messages.errorAlreadyExists", "name", m.i18n.P("globals.terms.customAttribute")), nil)
}
@@ -92,7 +92,7 @@ func (m *Manager) Create(attr models.CustomAttribute) error {
// Update updates a custom attribute by ID.
func (m *Manager) Update(id int, attr models.CustomAttribute) error {
if _, err := m.q.UpdateCustomAttribute.Exec(id, attr.AppliesTo, attr.Name, attr.Description, pq.Array(attr.Values), attr.DataType, attr.Regex); err != nil {
if _, err := m.q.UpdateCustomAttribute.Exec(id, attr.AppliesTo, attr.Name, attr.Description, pq.Array(attr.Values), attr.DataType, attr.Regex, attr.RegexHint); err != nil {
m.lo.Error("error updating custom attribute", "error", err)
return envelope.NewError(envelope.GeneralError, m.i18n.Ts("globals.messages.errorUpdating", "name", "{globals.terms.customAttribute}"), nil)
}

View File

@@ -17,4 +17,5 @@ type CustomAttribute struct {
Values pq.StringArray `db:"values" json:"values"`
DataType string `db:"data_type" json:"data_type"`
Regex string `db:"regex" json:"regex"`
RegexHint string `db:"regex_hint" json:"regex_hint"`
}

View File

@@ -9,7 +9,8 @@ SELECT
key,
values,
data_type,
regex
regex,
regex_hint
FROM
custom_attribute_definitions
WHERE
@@ -30,7 +31,8 @@ SELECT
key,
values,
data_type,
regex
regex,
regex_hint
FROM
custom_attribute_definitions
WHERE
@@ -38,9 +40,9 @@ WHERE
-- name: insert-custom-attribute
INSERT INTO
custom_attribute_definitions (applies_to, name, description, key, values, data_type, regex)
custom_attribute_definitions (applies_to, name, description, key, values, data_type, regex, regex_hint)
VALUES
($1, $2, $3, $4, $5, $6, $7)
($1, $2, $3, $4, $5, $6, $7, $8)
-- name: delete-custom-attribute
DELETE FROM
@@ -58,6 +60,7 @@ SET
values = $5,
data_type = $6,
regex = $7,
regex_hint = $8,
updated_at = NOW()
WHERE
id = $1;

View File

@@ -98,11 +98,17 @@ func V0_6_0(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error {
values TEXT[] DEFAULT '{}'::TEXT[] NOT NULL,
data_type TEXT NOT NULL,
regex TEXT NULL,
regex_hint TEXT NULL,
CONSTRAINT constraint_custom_attribute_definitions_on_name CHECK (length("name") <= 140),
CONSTRAINT constraint_custom_attribute_definitions_on_description CHECK (length(description) <= 300),
CONSTRAINT constraint_custom_attribute_definitions_on_key CHECK (length(key) <= 140),
CONSTRAINT constraint_custom_attribute_definitions_on_applies_to CHECK (length(applies_to) <= 50),
CONSTRAINT constraint_custom_attribute_definitions_on_data_type CHECK (length(data_type) <= 100),
CONSTRAINT constraint_custom_attribute_definitions_on_regex CHECK (length(regex) <= 1000),
CONSTRAINT constraint_custom_attribute_definitions_on_regex_hint CHECK (length(regex_hint) <= 1000),
CONSTRAINT constraint_custom_attribute_definitions_key_applies_to_unique UNIQUE (key, applies_to)
);
`)
if err != nil {
return err

View File

@@ -508,9 +508,14 @@ CREATE TABLE custom_attribute_definitions (
values TEXT[] DEFAULT '{}'::TEXT[] NOT NULL,
data_type TEXT NOT NULL,
regex TEXT NULL,
regex_hint TEXT NULL,
CONSTRAINT constraint_custom_attribute_definitions_on_name CHECK (length("name") <= 140),
CONSTRAINT constraint_custom_attribute_definitions_on_description CHECK (length(description) <= 300),
CONSTRAINT constraint_custom_attribute_definitions_on_key CHECK (length(key) <= 140),
CONSTRAINT constraint_custom_attribute_definitions_on_applies_to CHECK (length(applies_to) <= 50),
CONSTRAINT constraint_custom_attribute_definitions_on_data_type CHECK (length(data_type) <= 100),
CONSTRAINT constraint_custom_attribute_definitions_on_regex CHECK (length(regex) <= 1000),
CONSTRAINT constraint_custom_attribute_definitions_on_regex_hint CHECK (length(regex_hint) <= 1000),
CONSTRAINT constraint_custom_attribute_definitions_key_applies_to_unique UNIQUE (key, applies_to)
);